├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src └── main ├── java └── de │ └── howaner │ └── FakeMobs │ ├── FakeMobsPlugin.java │ ├── adjuster │ └── MyWorldAccess.java │ ├── command │ └── FakeMobCommand.java │ ├── event │ ├── PlayerInteractFakeMobEvent.java │ ├── RemoveFakeMobEvent.java │ └── SpawnFakeMobEvent.java │ ├── interact │ ├── InteractAction.java │ ├── InteractCommand.java │ ├── InteractExp.java │ ├── InteractItem.java │ ├── InteractText.java │ └── InteractType.java │ ├── listener │ ├── InteractListener.java │ ├── MobListener.java │ └── ProtocolListener.java │ ├── merchant │ ├── Merchant.java │ ├── MerchantOffer.java │ ├── NMSMerchant.java │ └── ReflectionUtils.java │ └── util │ ├── Cache.java │ ├── Config.java │ ├── DataWatchCreator.java │ ├── FakeMob.java │ ├── ItemParser.java │ ├── LookUpdate.java │ ├── MobInventory.java │ ├── MobShop.java │ └── SkinQueue.java └── resources └── plugin.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Binary 2 | bin/ 3 | 4 | # Eclipse 5 | .classpath 6 | .project 7 | .settings/ 8 | 9 | # Intellij 10 | .idea/ 11 | *.iml 12 | *.iws 13 | 14 | # Maven 15 | log/ 16 | target/ 17 | 18 | # Mac 19 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FakeMobs 2 | ======== 3 | 4 | You can spawn Fake Mobs 5 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | de.howaner 5 | FakeMobs 6 | 1.9.0 7 | FakeMobs 8 | A simple npc plugin 9 | jar 10 | 11 | UTF-8 12 | 13 | 14 | 15 | 16 | spigot-repo 17 | https://hub.spigotmc.org/nexus/content/groups/public/ 18 | 19 | 20 | dmulloy2-repo 21 | http://repo.dmulloy2.net/content/groups/public/ 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | maven-clean-plugin 30 | 2.5 31 | 32 | 33 | auto-clean 34 | initialize 35 | 36 | clean 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-compiler-plugin 45 | 2.5.1 46 | 47 | 1.6 48 | 1.6 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-jar-plugin 55 | 2.4 56 | 57 | ${project.artifactId} 58 | 59 | 60 | 61 | 62 | 63 | 64 | src/main/resources 65 | true 66 | 67 | 68 | 69 | 70 | 71 | 72 | org.spigotmc 73 | spigot-api 74 | 1.8.3-R0.1-SNAPSHOT 75 | 76 | 77 | com.comphenix.protocol 78 | ProtocolLib 79 | 3.6.4 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/FakeMobsPlugin.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs; 2 | 3 | import com.comphenix.protocol.ProtocolLibrary; 4 | import com.comphenix.protocol.ProtocolManager; 5 | import com.comphenix.protocol.wrappers.WrappedGameProfile; 6 | import com.comphenix.protocol.wrappers.WrappedSignedProperty; 7 | import com.google.common.collect.ArrayListMultimap; 8 | import com.google.common.collect.Multimap; 9 | import de.howaner.FakeMobs.adjuster.MyWorldAccess; 10 | import de.howaner.FakeMobs.command.FakeMobCommand; 11 | import de.howaner.FakeMobs.event.RemoveFakeMobEvent; 12 | import de.howaner.FakeMobs.event.SpawnFakeMobEvent; 13 | import de.howaner.FakeMobs.interact.InteractAction; 14 | import de.howaner.FakeMobs.interact.InteractType; 15 | import de.howaner.FakeMobs.listener.InteractListener; 16 | import de.howaner.FakeMobs.listener.MobListener; 17 | import de.howaner.FakeMobs.listener.ProtocolListener; 18 | import de.howaner.FakeMobs.merchant.MerchantOffer; 19 | import de.howaner.FakeMobs.merchant.ReflectionUtils; 20 | import de.howaner.FakeMobs.util.Cache; 21 | import de.howaner.FakeMobs.util.Config; 22 | import de.howaner.FakeMobs.util.FakeMob; 23 | import de.howaner.FakeMobs.util.LookUpdate; 24 | import de.howaner.FakeMobs.util.MobInventory; 25 | import de.howaner.FakeMobs.util.MobShop; 26 | import de.howaner.FakeMobs.util.SkinQueue; 27 | import java.io.File; 28 | import java.lang.reflect.Field; 29 | import java.util.ArrayList; 30 | import java.util.HashMap; 31 | import java.util.List; 32 | import java.util.Map; 33 | import java.util.Map.Entry; 34 | import java.util.logging.Level; 35 | import java.util.logging.Logger; 36 | import org.bukkit.Bukkit; 37 | import org.bukkit.Chunk; 38 | import org.bukkit.Location; 39 | import org.bukkit.Material; 40 | import org.bukkit.World; 41 | import org.bukkit.configuration.ConfigurationSection; 42 | import org.bukkit.configuration.file.YamlConfiguration; 43 | import org.bukkit.entity.EntityType; 44 | import org.bukkit.entity.Player; 45 | import org.bukkit.plugin.java.JavaPlugin; 46 | 47 | public class FakeMobsPlugin extends JavaPlugin { 48 | public static Logger log; 49 | private static FakeMobsPlugin instance; 50 | private ProtocolManager pManager; 51 | private final Map mobs = new HashMap(); 52 | private ProtocolListener pListener; 53 | private SkinQueue skinQueue; 54 | 55 | @Override 56 | public void onEnable() { 57 | instance = this; 58 | log = this.getLogger(); 59 | this.pManager = ProtocolLibrary.getProtocolManager(); 60 | this.loadMobsFile(); 61 | 62 | this.skinQueue = new SkinQueue(); 63 | this.skinQueue.start(); 64 | 65 | if (!Config.configFile.exists()) Config.save(); 66 | Config.load(); 67 | 68 | Bukkit.getPluginManager().registerEvents(new InteractListener(), this); 69 | Bukkit.getPluginManager().registerEvents(new MobListener(this), this); 70 | this.getCommand("FakeMob").setExecutor(new FakeMobCommand(this)); 71 | 72 | for (Player player : Bukkit.getOnlinePlayers()) 73 | this.updatePlayerView(player); 74 | 75 | Bukkit.getScheduler().scheduleAsyncRepeatingTask(this, new LookUpdate(this), 5L, 5L); 76 | this.pManager.addPacketListener(pListener = new ProtocolListener(this)); 77 | 78 | for (World world : Bukkit.getWorlds()) { 79 | MyWorldAccess.registerWorldAccess(world); 80 | } 81 | 82 | log.info("Plugin enabled!"); 83 | } 84 | 85 | @Override 86 | public void onDisable() { 87 | this.getProtocolManager().removePacketListener(pListener); 88 | Bukkit.getScheduler().cancelTasks(this); 89 | for (FakeMob mob : this.getMobs()) 90 | for (Player player : Bukkit.getOnlinePlayers()) 91 | if (mob.getWorld() == player.getWorld()) 92 | mob.sendDestroyPacket(player); 93 | 94 | for (World world : Bukkit.getWorlds()) { 95 | MyWorldAccess.unregisterWorldAccess(world); 96 | } 97 | 98 | log.info("Plugin disabled!"); 99 | } 100 | 101 | public SkinQueue getSkinQueue() { 102 | return this.skinQueue; 103 | } 104 | 105 | public boolean existsMob(int id) { 106 | return this.mobs.containsKey(id); 107 | } 108 | 109 | public FakeMob getMob(Location loc) { 110 | for (FakeMob mob : this.getMobs()) { 111 | if (mob.getLocation().getWorld() == loc.getWorld() && 112 | mob.getLocation().getBlockX() == loc.getBlockX() && 113 | mob.getLocation().getBlockY() == loc.getBlockY() && 114 | mob.getLocation().getBlockZ() == loc.getBlockZ()) 115 | return mob; 116 | } 117 | return null; 118 | } 119 | 120 | public boolean isMobOnLocation(Location loc) { 121 | return (this.getMob(loc) != null); 122 | } 123 | 124 | public FakeMob getMob(int id) { 125 | return this.mobs.get(id); 126 | } 127 | 128 | public void removeMob(int id) { 129 | FakeMob mob = this.mobs.get(id); 130 | if (mob == null) return; 131 | 132 | RemoveFakeMobEvent event = new RemoveFakeMobEvent(mob); 133 | Bukkit.getPluginManager().callEvent(event); 134 | 135 | for (Player player : mob.getWorld().getPlayers()) { 136 | mob.unloadPlayer(player); 137 | } 138 | 139 | Map selectedMap = new HashMap(); 140 | selectedMap.putAll(Cache.selectedMobs); 141 | for (Entry e : selectedMap.entrySet()) { 142 | if (e.getValue() == mob) 143 | Cache.selectedMobs.remove(e.getKey()); 144 | } 145 | 146 | this.mobs.remove(id); 147 | this.saveMobsFile(); 148 | } 149 | 150 | public FakeMob spawnMob(Location loc, EntityType type) { 151 | return this.spawnMob(loc, type, null); 152 | } 153 | 154 | public FakeMob spawnMob(Location loc, EntityType type, String customName) { 155 | if (!type.isAlive()) return null; 156 | 157 | int id = this.getNewId(); 158 | FakeMob mob = new FakeMob(id, loc, type); 159 | mob.setCustomName(customName); 160 | 161 | SpawnFakeMobEvent event = new SpawnFakeMobEvent(loc, type, mob); 162 | Bukkit.getPluginManager().callEvent(event); 163 | if (event.isCancelled()) return null; 164 | 165 | for (Player player : loc.getWorld().getPlayers()) { 166 | if (mob.isInRange(player)) { 167 | mob.loadPlayer(player); 168 | } 169 | } 170 | 171 | this.mobs.put(id, mob); 172 | this.saveMobsFile(); 173 | return mob; 174 | } 175 | 176 | public FakeMob spawnPlayer(Location loc, String name) { 177 | return this.spawnPlayer(loc, name, (Multimap) null); 178 | } 179 | 180 | public FakeMob spawnPlayer(Location loc, String name, Player skin) { 181 | return this.spawnPlayer(loc, name, WrappedGameProfile.fromPlayer(skin).getProperties()); 182 | } 183 | 184 | public FakeMob spawnPlayer(Location loc, String name, Multimap skin) { 185 | int id = this.getNewId(); 186 | FakeMob mob = new FakeMob(id, loc, EntityType.PLAYER); 187 | mob.setCustomName(name); 188 | mob.setPlayerSkin(skin); 189 | 190 | SpawnFakeMobEvent event = new SpawnFakeMobEvent(loc, EntityType.PLAYER, mob); 191 | Bukkit.getPluginManager().callEvent(event); 192 | if (event.isCancelled()) return null; 193 | 194 | for (Player player : loc.getWorld().getPlayers()) { 195 | if (mob.isInRange(player)) { 196 | mob.loadPlayer(player); 197 | } 198 | } 199 | 200 | this.mobs.put(id, mob); 201 | this.saveMobsFile(); 202 | return mob; 203 | } 204 | 205 | public int getNewId() { 206 | int id = -1; 207 | for (FakeMob mob : this.getMobs()) 208 | if (mob.getId() > id) 209 | id = mob.getId(); 210 | return id+1; 211 | } 212 | 213 | public List getMobs() { 214 | List mobList = new ArrayList(); 215 | mobList.addAll(this.mobs.values()); 216 | return mobList; 217 | } 218 | 219 | public Map getMobsMap() { 220 | return this.mobs; 221 | } 222 | 223 | /** Called every chunk move */ 224 | public void updatePlayerView(Player player) { 225 | for (FakeMob mob : this.getMobs()) { 226 | if (mob.isInRange(player)) { 227 | mob.loadPlayer(player); 228 | } else { 229 | mob.unloadPlayer(player); 230 | } 231 | } 232 | } 233 | 234 | public List getMobsInRadius(Location loc, int radius) { 235 | List mobList = new ArrayList(); 236 | for (FakeMob mob : this.getMobs()) { 237 | if (mob.getWorld() == loc.getWorld() && mob.getLocation().distance(loc) <= radius) { 238 | mobList.add(mob); 239 | } 240 | } 241 | 242 | return mobList; 243 | } 244 | 245 | public List getMobsInChunk(World world, int chunkX, int chunkZ) { 246 | List mobList = new ArrayList(); 247 | 248 | for (FakeMob mob : this.getMobs()) { 249 | Chunk chunk = mob.getLocation().getChunk(); 250 | if (mob.getWorld() == world && chunk.getX() == chunkX && chunk.getZ() == chunkZ) { 251 | mobList.add(mob); 252 | } 253 | } 254 | 255 | return mobList; 256 | } 257 | 258 | public static FakeMobsPlugin getPlugin() { 259 | return instance; 260 | } 261 | 262 | public ProtocolManager getProtocolManager() { 263 | return this.pManager; 264 | } 265 | 266 | public void loadMobsFile() { 267 | this.mobs.clear(); 268 | YamlConfiguration config = YamlConfiguration.loadConfiguration(new File("plugins/FakeMobs/mobs.yml")); 269 | 270 | for (String key : config.getKeys(false)) { 271 | ConfigurationSection section = config.getConfigurationSection(key); 272 | int id = Integer.parseInt(key); 273 | Location loc = new Location(Bukkit.getWorld(section.getString("World")), 274 | section.getDouble("X"), 275 | section.getDouble("Y"), 276 | section.getDouble("Z"), 277 | Float.parseFloat(section.getString("Yaw")), 278 | Float.parseFloat(section.getString("Pitch"))); 279 | EntityType type = EntityType.valueOf(section.getString("Type").toUpperCase()); 280 | FakeMob mob = new FakeMob(id, loc, type); 281 | if (section.isSet("Name") && section.getString("Name").length() <= 16) 282 | mob.setCustomName(section.getString("Name")); 283 | mob.setSitting(section.getBoolean("Sitting")); 284 | if (section.contains("Invisibility")) 285 | mob.setInvisibility(section.getBoolean("Invisibility")); 286 | mob.setPlayerLook(section.getBoolean("PlayerLook")); 287 | 288 | if (section.contains("Inventory")) { 289 | MobInventory inv = new MobInventory(); 290 | ConfigurationSection invSection = section.getConfigurationSection("Inventory"); 291 | if (invSection.contains("ItemInHand")) 292 | inv.setItemInHand(invSection.getItemStack("ItemInHand")); 293 | if (invSection.contains("Boots")) 294 | inv.setBoots(invSection.getItemStack("Boots")); 295 | if (invSection.contains("Leggings")) 296 | inv.setLeggings(invSection.getItemStack("Leggings")); 297 | if (invSection.contains("ChestPlate")) 298 | inv.setChestPlate(invSection.getItemStack("ChestPlate")); 299 | if (invSection.contains("Helmet")) 300 | inv.setHelmet(invSection.getItemStack("Helmet")); 301 | 302 | mob.setInventory(inv); 303 | } 304 | 305 | if (section.contains("Shop")) { 306 | ConfigurationSection shopSection = section.getConfigurationSection("Shop"); 307 | MobShop shop = new MobShop(); 308 | for (String key2 : shopSection.getKeys(false)) { 309 | ConfigurationSection itemSection = shopSection.getConfigurationSection(key2); 310 | MerchantOffer offer = new MerchantOffer(itemSection.getItemStack("Item1"), 311 | ((itemSection.contains("Item2")) ? itemSection.getItemStack("Item2") : null), 312 | itemSection.getItemStack("Output")); 313 | shop.addItem(offer); 314 | } 315 | mob.setShop(shop); 316 | } 317 | 318 | if (section.contains("Interacts")) { 319 | ConfigurationSection interactsSection = section.getConfigurationSection("Interacts"); 320 | for (String key2 : interactsSection.getKeys(false)) { 321 | ConfigurationSection interactSection = interactsSection.getConfigurationSection(key2); 322 | InteractType interactType = InteractType.getByName(interactSection.getString("Type")); 323 | if (interactType == null) { 324 | log.warning("Interact Type " + interactSection.getString("Type") + " not exists!"); 325 | continue; 326 | } 327 | InteractAction action; 328 | try { 329 | action = interactType.getActionClass().newInstance(); 330 | } catch (Exception e) { 331 | e.printStackTrace(); 332 | continue; 333 | } 334 | action.loadFromConfig(interactSection); 335 | mob.addInteractAction(action); 336 | } 337 | } 338 | 339 | if (mob.getType() == EntityType.PLAYER && section.contains("Skin")) { 340 | ConfigurationSection skinsSection = section.getConfigurationSection("Skin"); 341 | Multimap skins = ArrayListMultimap.create(); 342 | 343 | for (String k : skinsSection.getKeys(false)) { 344 | ConfigurationSection skinSection = skinsSection.getConfigurationSection(k); 345 | WrappedSignedProperty property = new WrappedSignedProperty(skinSection.getString("Name"), skinSection.getString("Value"), skinSection.getString("Signature")); 346 | skins.put(property.getName(), property); 347 | } 348 | 349 | mob.setPlayerSkin(skins); 350 | } 351 | 352 | this.mobs.put(id, mob); 353 | } 354 | 355 | log.info("Loaded " + this.mobs.size() + " mobs!"); 356 | } 357 | 358 | public void saveMobsFile() { 359 | YamlConfiguration config = new YamlConfiguration(); 360 | 361 | for (FakeMob mob : this.getMobs()) { 362 | ConfigurationSection section = config.createSection(String.valueOf(mob.getId())); 363 | section.set("World", mob.getWorld().getName()); 364 | section.set("X", mob.getLocation().getX()); 365 | section.set("Y", mob.getLocation().getY()); 366 | section.set("Z", mob.getLocation().getZ()); 367 | section.set("Yaw", mob.getLocation().getYaw()); 368 | section.set("Pitch", mob.getLocation().getPitch()); 369 | section.set("Type", mob.getType().name()); 370 | if (mob.getCustomName() != null) 371 | section.set("Name", mob.getCustomName()); 372 | section.set("Sitting", mob.isSitting()); 373 | section.set("Invisibility", mob.isInvisibility()); 374 | section.set("PlayerLook", mob.isPlayerLook()); 375 | 376 | if (!mob.getInventory().isEmpty()) { 377 | ConfigurationSection invSection = section.createSection("Inventory"); 378 | if (mob.getInventory().getItemInHand() != null && mob.getInventory().getItemInHand().getType() != Material.AIR) 379 | invSection.set("ItemInHand", mob.getInventory().getItemInHand()); 380 | if (mob.getInventory().getBoots() != null && mob.getInventory().getBoots().getType() != Material.AIR) 381 | invSection.set("Boots", mob.getInventory().getBoots()); 382 | if (mob.getInventory().getLeggings() != null && mob.getInventory().getLeggings().getType() != Material.AIR) 383 | invSection.set("Leggings", mob.getInventory().getLeggings()); 384 | if (mob.getInventory().getChestPlate() != null && mob.getInventory().getChestPlate().getType() != Material.AIR) 385 | invSection.set("ChestPlate", mob.getInventory().getChestPlate()); 386 | if (mob.getInventory().getHelmet() != null && mob.getInventory().getHelmet().getType() != Material.AIR) 387 | invSection.set("Helmet", mob.getInventory().getHelmet()); 388 | } 389 | 390 | if (mob.haveShop()) { 391 | ConfigurationSection shopSection = section.createSection("Shop"); 392 | for (int i = 0; i < mob.getShop().getItems().size(); i++) { 393 | ConfigurationSection itemSection = shopSection.createSection(String.valueOf(i)); 394 | MerchantOffer offer = mob.getShop().getItems().get(i); 395 | itemSection.set("Item1", offer.getFirstInput()); 396 | if (offer.getSecondInput() != null) itemSection.set("Item2", offer.getSecondInput()); 397 | itemSection.set("Output", offer.getOutput()); 398 | } 399 | } 400 | 401 | if (!mob.getInteractActions().isEmpty()) { 402 | ConfigurationSection interactsSection = section.createSection("Interacts"); 403 | for (int i = 0; i < mob.getInteractActions().size(); i++) { 404 | InteractAction action = mob.getInteractActions().get(i); 405 | ConfigurationSection interactSection = interactsSection.createSection("#" + String.valueOf(i)); 406 | interactSection.set("Type", action.getType().name()); 407 | action.saveToConfig(interactSection); 408 | } 409 | } 410 | 411 | if (mob.getType() == EntityType.PLAYER && mob.getPlayerSkin() != null && !mob.getPlayerSkin().isEmpty()) { 412 | ConfigurationSection skinsSection = section.createSection("Skin"); 413 | int i = 0; 414 | 415 | for (WrappedSignedProperty property : mob.getPlayerSkin().values()) { 416 | ConfigurationSection skinSection = skinsSection.createSection("Property-" + String.valueOf(i)); 417 | skinSection.set("Name", property.getName()); 418 | skinSection.set("Value", property.getValue()); 419 | skinSection.set("Signature", property.getSignature()); 420 | i++; 421 | } 422 | } 423 | } 424 | 425 | try { 426 | config.save(new File("plugins/FakeMobs/mobs.yml")); 427 | } catch (Exception e) { 428 | e.printStackTrace(); 429 | } 430 | } 431 | 432 | public void adjustEntityCount() { 433 | try { 434 | Class entityClass = Class.forName(ReflectionUtils.getNMSPackageName() + ".Entity"); 435 | 436 | Field field = entityClass.getDeclaredField("entityCount"); 437 | field.setAccessible(true); 438 | int currentCount = field.getInt(null); 439 | 440 | if (currentCount >= 2300) { 441 | while (this.existsMob(currentCount - 2300)) { 442 | currentCount++; 443 | } 444 | 445 | field.set(null, currentCount); 446 | } 447 | } catch (Exception ex) { 448 | this.getLogger().log(Level.WARNING, "Can't adjust entity count", ex); 449 | } 450 | } 451 | 452 | } 453 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/adjuster/MyWorldAccess.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.adjuster; 2 | 3 | import de.howaner.FakeMobs.FakeMobsPlugin; 4 | import de.howaner.FakeMobs.merchant.ReflectionUtils; 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Method; 7 | import java.lang.reflect.Proxy; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | import java.util.logging.Level; 11 | import org.bukkit.Bukkit; 12 | import org.bukkit.World; 13 | 14 | public class MyWorldAccess implements java.lang.reflect.InvocationHandler { 15 | 16 | @Override 17 | public Object invoke(Object proxy, Method m, Object[] args) { 18 | try { 19 | if (m == null || m.getName() == null) return null; 20 | Class entityClass = Class.forName(ReflectionUtils.getNMSPackageName() + ".Entity"); 21 | 22 | if (m.getName().equals("a") && args.length == 1 && args[0] != null && classInstance(args[0].getClass(), entityClass)) { 23 | this.onAddEntity(); 24 | } 25 | } catch (Exception e) { 26 | e.printStackTrace(); 27 | } 28 | return null; 29 | } 30 | 31 | private boolean classInstance(Class clazz, Class instance) { 32 | while (clazz != null) { 33 | if (clazz == instance) { 34 | return true; 35 | } 36 | 37 | clazz = clazz.getSuperclass(); 38 | } 39 | return false; 40 | } 41 | 42 | public void onAddEntity() { 43 | FakeMobsPlugin.getPlugin().adjustEntityCount(); 44 | } 45 | 46 | private static List getAccessList(World world) throws Exception { 47 | Class craftWorldClass = Class.forName(ReflectionUtils.getOBCPackageName() + ".CraftWorld"); 48 | Class worldClass = Class.forName(ReflectionUtils.getNMSPackageName() + ".World"); 49 | 50 | Object nmsWorld; 51 | { 52 | Method method = craftWorldClass.getDeclaredMethod("getHandle"); 53 | method.setAccessible(true); 54 | nmsWorld = method.invoke(world); 55 | } 56 | 57 | List accessList; 58 | { 59 | Field field = worldClass.getDeclaredField("u"); 60 | field.setAccessible(true); 61 | accessList = (List) field.get(nmsWorld); 62 | } 63 | 64 | return accessList; 65 | } 66 | 67 | public static void registerWorldAccess(World world) { 68 | try { 69 | Class iWorldAccessClass = Class.forName(ReflectionUtils.getNMSPackageName() + ".IWorldAccess"); 70 | List accessList = getAccessList(world); 71 | 72 | Object myAccess = Proxy.newProxyInstance(Bukkit.class.getClassLoader(), new Class[] { iWorldAccessClass }, new MyWorldAccess()); 73 | accessList.add(myAccess); 74 | FakeMobsPlugin.getPlugin().getLogger().log(Level.INFO, "Setted up entity adjuster on world {0}!", world.getName()); 75 | } catch (Exception ex) { 76 | FakeMobsPlugin.getPlugin().getLogger().log(Level.WARNING, "Can't register entity adjuster.", ex); 77 | } 78 | } 79 | 80 | public static void unregisterWorldAccess(World world) { 81 | try { 82 | Class iWorldAccessClass = Class.forName(ReflectionUtils.getNMSPackageName() + ".IWorldAccess"); 83 | Class proxyClass = Proxy.getProxyClass(Bukkit.class.getClassLoader(), new Class[] { iWorldAccessClass }); 84 | 85 | List accessList = getAccessList(world); 86 | 87 | Iterator itr = accessList.iterator(); 88 | while (itr.hasNext()) { 89 | if (itr.next().getClass() == proxyClass) { 90 | itr.remove(); 91 | FakeMobsPlugin.getPlugin().getLogger().log(Level.INFO, "Removed entity adjuster from world {0}", world.getName()); 92 | } 93 | } 94 | } catch (Exception ex) { 95 | FakeMobsPlugin.getPlugin().getLogger().log(Level.WARNING, "Can't unregister entity adjuster.", ex); 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/command/FakeMobCommand.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.command; 2 | 3 | import de.howaner.FakeMobs.FakeMobsPlugin; 4 | import de.howaner.FakeMobs.interact.InteractAction; 5 | import de.howaner.FakeMobs.interact.InteractType; 6 | import de.howaner.FakeMobs.merchant.MerchantOffer; 7 | import de.howaner.FakeMobs.util.Cache; 8 | import de.howaner.FakeMobs.util.FakeMob; 9 | import de.howaner.FakeMobs.util.ItemParser; 10 | import de.howaner.FakeMobs.util.MobShop; 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import org.bukkit.ChatColor; 14 | import org.bukkit.Location; 15 | import org.bukkit.command.Command; 16 | import org.bukkit.command.CommandExecutor; 17 | import org.bukkit.command.CommandSender; 18 | import org.bukkit.entity.EntityType; 19 | import org.bukkit.entity.Player; 20 | import org.bukkit.inventory.ItemStack; 21 | 22 | public class FakeMobCommand implements CommandExecutor { 23 | private FakeMobsPlugin plugin; 24 | 25 | public FakeMobCommand(FakeMobsPlugin plugin) { 26 | this.plugin = plugin; 27 | } 28 | 29 | @Override 30 | public boolean onCommand(CommandSender sender, Command cmd, String cmdLabel, String[] args) { 31 | if (!(sender instanceof Player)) { 32 | sender.sendMessage(ChatColor.RED + "You are not a Player!"); 33 | return true; 34 | } 35 | Player player = (Player) sender; 36 | if (args.length == 0) return false; 37 | if (args[0].equalsIgnoreCase("create")) { 38 | if (args.length != 2) return false; 39 | if (!player.hasPermission("FakeMobs.create")) { 40 | player.sendMessage(ChatColor.RED + "No permission!"); 41 | return true; 42 | } 43 | Location loc = player.getLocation(); 44 | if (this.plugin.isMobOnLocation(loc)) { 45 | player.sendMessage(ChatColor.RED + "Here is already a Mob!"); 46 | return true; 47 | } 48 | EntityType type; 49 | try { 50 | type = EntityType.valueOf(args[1].toUpperCase()); 51 | } catch (Exception e) { 52 | player.sendMessage(ChatColor.RED + args[1] + " is not a Entity!"); 53 | StringBuilder entityBuilder = new StringBuilder(); 54 | boolean komma = false; 55 | for (int i = 0; i < EntityType.values().length; i++) { 56 | EntityType t = EntityType.values()[i]; 57 | if (t == null || !t.isAlive() || t.getName() == null) continue; 58 | if (komma) entityBuilder.append(", "); 59 | entityBuilder.append(t.name()); 60 | komma = true; 61 | } 62 | player.sendMessage(ChatColor.GOLD + "Available Entitys: " + ChatColor.WHITE + entityBuilder.toString()); 63 | return true; 64 | } 65 | if (!type.isAlive()) { 66 | player.sendMessage(ChatColor.RED + "This entity is not alive!"); 67 | return true; 68 | } 69 | FakeMob mob = this.plugin.spawnMob(loc, type); 70 | if (mob == null) { 71 | player.sendMessage(ChatColor.RED + "An error occurred while creating the Mob!"); 72 | return true; 73 | } 74 | player.sendMessage(ChatColor.GREEN + "Created and selected Mob with ID " + ChatColor.GRAY + "#" + mob.getId()); 75 | Cache.selectedMobs.put(player, mob); 76 | return true; 77 | } else if (args[0].equalsIgnoreCase("select")) { 78 | if (args.length != 1 && args.length != 2) return false; 79 | if (!player.hasPermission("FakeMobs.select")) { 80 | player.sendMessage(ChatColor.RED + "No permission!"); 81 | return true; 82 | } 83 | if (args.length == 2) { 84 | int id; 85 | try { 86 | id = Integer.valueOf(args[1]); 87 | } catch (Exception e) { 88 | player.sendMessage(ChatColor.RED + "Please enter a valid Id!"); 89 | return true; 90 | } 91 | FakeMob mob = this.plugin.getMob(id); 92 | if (mob == null) { 93 | player.sendMessage(ChatColor.RED + "A Mob with ID " + ChatColor.GRAY + "#" + id + ChatColor.RED + " don't exists!"); 94 | return true; 95 | } 96 | Cache.selectedMobs.put(player, mob); 97 | player.sendMessage(ChatColor.GREEN + "Mob " + ChatColor.GRAY + "#" + id + ChatColor.GREEN + " selected!"); 98 | return true; 99 | } 100 | 101 | if (Cache.selectedMobs.containsKey(player) && Cache.selectedMobs.get(player) == null) { 102 | Cache.selectedMobs.remove(player); 103 | player.sendMessage(ChatColor.GOLD + "Selection cancelled!"); 104 | } else { 105 | Cache.selectedMobs.put(player, null); 106 | player.sendMessage(ChatColor.GREEN + "Click on the FakeMob!"); 107 | } 108 | return true; 109 | } else if (args[0].equalsIgnoreCase("name")) { 110 | if (args.length < 2) return false; 111 | if (!player.hasPermission("FakeMobs.name")) { 112 | player.sendMessage(ChatColor.RED + "No permission!"); 113 | return true; 114 | } 115 | FakeMob mob = Cache.selectedMobs.get(player); 116 | if (mob == null) { 117 | player.sendMessage(ChatColor.RED + "You haven't a Selection!"); 118 | return true; 119 | } 120 | StringBuilder textBuilder = new StringBuilder(); 121 | for (int i = 1; i < args.length; i++) { 122 | if (i != 1) textBuilder.append(" "); 123 | textBuilder.append(args[i]); 124 | } 125 | String text = ChatColor.translateAlternateColorCodes('&', textBuilder.toString()); 126 | if (text.length() > 32) { 127 | player.sendMessage(ChatColor.RED + "Name too long!"); 128 | return true; 129 | } 130 | if (text.equalsIgnoreCase("none")) { 131 | mob.setCustomName(null); 132 | player.sendMessage(ChatColor.GREEN + "Mob Name deleted!"); 133 | } else { 134 | mob.setCustomName(text); 135 | player.sendMessage(ChatColor.GREEN + "Mob Name set to " + ChatColor.GRAY + text + ChatColor.GREEN + "!"); 136 | } 137 | mob.updateCustomName(); 138 | this.plugin.saveMobsFile(); 139 | return true; 140 | } else if (args[0].equalsIgnoreCase("sitting")) { 141 | if (args.length < 1) return false; 142 | if (!player.hasPermission("FakeMobs.sitting")) { 143 | player.sendMessage(ChatColor.RED + "No permission!"); 144 | return true; 145 | } 146 | FakeMob mob = Cache.selectedMobs.get(player); 147 | if (mob == null) { 148 | player.sendMessage(ChatColor.RED + "You haven't a Selection!"); 149 | return true; 150 | } 151 | if (mob.getType() != EntityType.OCELOT && mob.getType() != EntityType.WOLF && mob.getType() != EntityType.PLAYER) { 152 | player.sendMessage(ChatColor.RED + "Only pets and players can sit!"); 153 | return true; 154 | } 155 | mob.setSitting(!mob.isSitting()); 156 | mob.updateMetadata(); 157 | this.plugin.saveMobsFile(); 158 | player.sendMessage(ChatColor.GREEN + "Sitting Status changed: " + ChatColor.GRAY + ((mob.isSitting()) ? "on" : "off")); 159 | return true; 160 | } else if (args[0].equalsIgnoreCase("invisibility")) { 161 | if (args.length < 1) return false; 162 | if (!player.hasPermission("FakeMobs.invisibility")) { 163 | player.sendMessage(ChatColor.RED + "No permission!"); 164 | return true; 165 | } 166 | FakeMob mob = Cache.selectedMobs.get(player); 167 | if (mob == null) { 168 | player.sendMessage(ChatColor.RED + "You haven't a Selection!"); 169 | return true; 170 | } 171 | mob.setInvisibility(!mob.isInvisibility()); 172 | mob.updateMetadata(); 173 | this.plugin.saveMobsFile(); 174 | player.sendMessage(ChatColor.GREEN + "Invisibility Status changed: " + ChatColor.GRAY + ((mob.isInvisibility()) ? "on" : "off")); 175 | return true; 176 | } else if (args[0].equalsIgnoreCase("look")) { 177 | if (args.length < 1) return false; 178 | if (!player.hasPermission("FakeMobs.look")) { 179 | player.sendMessage(ChatColor.RED + "No permission!"); 180 | return true; 181 | } 182 | FakeMob mob = Cache.selectedMobs.get(player); 183 | if (mob == null) { 184 | player.sendMessage(ChatColor.RED + "You haven't a Selection!"); 185 | return true; 186 | } 187 | mob.setPlayerLook(!mob.isPlayerLook()); 188 | if (mob.isPlayerLook()) 189 | mob.sendLookPacket(player, player.getLocation()); 190 | this.plugin.saveMobsFile(); 191 | player.sendMessage(ChatColor.GREEN + "Player Look: " + ChatColor.GRAY + ((mob.isPlayerLook()) ? "on" : "off")); 192 | return true; 193 | } else if (args[0].equalsIgnoreCase("teleport")) { 194 | if (args.length != 1) return false; 195 | if (!player.hasPermission("FakeMobs.teleport")) { 196 | player.sendMessage(ChatColor.RED + "No permission!"); 197 | return true; 198 | } 199 | FakeMob mob = Cache.selectedMobs.get(player); 200 | if (mob == null) { 201 | player.sendMessage(ChatColor.RED + "You haven't a Selection!"); 202 | return true; 203 | } 204 | mob.teleport(player.getLocation()); 205 | this.plugin.saveMobsFile(); 206 | player.sendMessage(ChatColor.GREEN + "Teleported Mob " + ChatColor.GRAY + "#" + mob.getId() + ChatColor.GREEN + "!"); 207 | return true; 208 | } else if (args[0].equalsIgnoreCase("skin")) { 209 | if (args.length != 2) return false; 210 | if (!player.hasPermission("FakeMobs.skin")) { 211 | player.sendMessage(ChatColor.RED + "No permission!"); 212 | return true; 213 | } 214 | 215 | FakeMob mob = Cache.selectedMobs.get(player); 216 | if (mob == null) { 217 | player.sendMessage(ChatColor.RED + "You don't have a selection!"); 218 | return true; 219 | } 220 | if (mob.getType() != EntityType.PLAYER) { 221 | player.sendMessage(ChatColor.RED + "Only players can have a skin!"); 222 | return true; 223 | } 224 | 225 | FakeMobsPlugin.getPlugin().getSkinQueue().addToQueue(mob, args[1]); 226 | player.sendMessage(ChatColor.GREEN + "Skin set!"); 227 | return true; 228 | } else if (args[0].equalsIgnoreCase("inv")) { 229 | if (args.length < 3) return false; 230 | if (!player.hasPermission("FakeMobs.inv")) { 231 | player.sendMessage(ChatColor.RED + "No permission!"); 232 | return true; 233 | } 234 | FakeMob mob = Cache.selectedMobs.get(player); 235 | if (mob == null) { 236 | player.sendMessage(ChatColor.RED + "You haven't a Selection!"); 237 | return true; 238 | } 239 | 240 | List itemArgs = new ArrayList(); 241 | for (int i = 2; i < args.length; i++) 242 | itemArgs.add(args[i]); 243 | 244 | ItemStack item; 245 | try { 246 | item = ItemParser.generateItemStack(itemArgs.toArray(new String[0])); 247 | } catch (Exception e) { 248 | player.sendMessage(ChatColor.RED + "Invalid item: " + e.getMessage()); 249 | return true; 250 | } 251 | 252 | if (args[1].equalsIgnoreCase("hand")) { 253 | mob.getInventory().setItemInHand(item); 254 | player.sendMessage(ChatColor.GOLD + "Setted Item in Hand to " + item.getType().name() + "!"); 255 | } else if (args[1].equalsIgnoreCase("boots")) { 256 | mob.getInventory().setBoots(item); 257 | player.sendMessage(ChatColor.GOLD + "Setted Boots to " + item.getType().name() + "!"); 258 | } else if (args[1].equalsIgnoreCase("leggings")) { 259 | mob.getInventory().setLeggings(item); 260 | player.sendMessage(ChatColor.GOLD + "Setted Leggings to " + item.getType().name() + "!"); 261 | } else if (args[1].equalsIgnoreCase("chestplate")) { 262 | mob.getInventory().setChestPlate(item); 263 | player.sendMessage(ChatColor.GOLD + "Setted ChestPlate to " + item.getType().name() + "!"); 264 | } else if (args[1].equalsIgnoreCase("helmet")) { 265 | mob.getInventory().setHelmet(item); 266 | player.sendMessage(ChatColor.GOLD + "Setted Helmet to " + item.getType().name() + "!"); 267 | } else 268 | return false; 269 | 270 | mob.updateInventory(); 271 | this.plugin.saveMobsFile(); 272 | return true; 273 | } else if (args[0].equalsIgnoreCase("shop")) { 274 | if (args.length < 2) return false; 275 | if (!player.hasPermission("FakeMobs.shop")) { 276 | player.sendMessage(ChatColor.RED + "No permission!"); 277 | return true; 278 | } 279 | FakeMob mob = Cache.selectedMobs.get(player); 280 | if (mob == null) { 281 | player.sendMessage(ChatColor.RED + "You haven't a Selection!"); 282 | return true; 283 | } 284 | if (args[1].equalsIgnoreCase("enable")) { 285 | if (args.length != 2) return false; 286 | if (mob.haveShop()) { 287 | player.sendMessage(ChatColor.RED + "This Mob have already a Shop!"); 288 | return true; 289 | } 290 | mob.setShop(new MobShop()); 291 | this.plugin.saveMobsFile(); 292 | player.sendMessage(ChatColor.GREEN + "Villager Shop enabled!"); 293 | return true; 294 | } else if (args[1].equalsIgnoreCase("disable")) { 295 | if (args.length != 2) return false; 296 | if (!mob.haveShop()) { 297 | player.sendMessage(ChatColor.RED + "This Mob haven't a Shop!"); 298 | return true; 299 | } 300 | mob.setShop(null); 301 | this.plugin.saveMobsFile(); 302 | player.sendMessage(ChatColor.GREEN + "Villager Shop removed!"); 303 | return true; 304 | } else if (args[1].equalsIgnoreCase("addItem")) { 305 | if (args.length < 3) return false; 306 | if (!mob.haveShop()) { 307 | player.sendMessage(ChatColor.RED + "This Mob haven't a Shop!"); 308 | return true; 309 | } 310 | 311 | StringBuilder builder = new StringBuilder(); 312 | for (int i = 2; i < args.length; i++) { 313 | if (i != 2) builder.append(" "); 314 | builder.append(args[i]); 315 | } 316 | 317 | String[] items = builder.toString().split(";"); 318 | if (items.length != 2 && args.length != 3) return false; 319 | 320 | String state = "input"; 321 | ItemStack item1, item2 = null, result; 322 | try { 323 | item1 = ItemParser.generateItemStack(items[0].split(" ")); 324 | 325 | if (items.length == 2) { 326 | state = "result"; 327 | result = ItemParser.generateItemStack(items[1].split(" ")); 328 | } else { 329 | state = "second input"; 330 | item2 = ItemParser.generateItemStack(items[1].split(" ")); 331 | 332 | state = "result"; 333 | result = ItemParser.generateItemStack(items[2].split(" ")); 334 | } 335 | } catch (Exception e) { 336 | player.sendMessage(ChatColor.RED + "Invalid " + state + ": " + e.getMessage()); 337 | return true; 338 | } 339 | 340 | MerchantOffer offer = new MerchantOffer(item1, item2, result); 341 | mob.getShop().addItem(offer); 342 | this.plugin.saveMobsFile(); 343 | player.sendMessage(ChatColor.GREEN + "Item added!"); 344 | return true; 345 | } else if (args[1].equalsIgnoreCase("removeItem")) { 346 | if (args.length != 3) return false; 347 | if (!mob.haveShop()) { 348 | player.sendMessage(ChatColor.RED + "This Mob haven't a Shop!"); 349 | return true; 350 | } 351 | int id; 352 | try { 353 | id = Integer.parseInt(args[2]); 354 | } catch (Exception e) { 355 | player.sendMessage(ChatColor.RED + args[2] + " isn't a Id!"); 356 | return true; 357 | } 358 | if (mob.getShop().getItem(id) == null) { 359 | player.sendMessage(ChatColor.RED + "Item " + ChatColor.GRAY + "#" + id + ChatColor.RED + " dont't exists!"); 360 | return true; 361 | } 362 | mob.getShop().removeItem(id); 363 | this.plugin.saveMobsFile(); 364 | player.sendMessage(ChatColor.GOLD + "Item " + ChatColor.GRAY + "#" + id + ChatColor.GOLD + " was removed!"); 365 | return true; 366 | } else if (args[1].equalsIgnoreCase("clear")) { 367 | if (args.length != 2) return false; 368 | if (!mob.haveShop()) { 369 | player.sendMessage(ChatColor.RED + "This Mob haven't a Shop!"); 370 | return true; 371 | } 372 | if (mob.getShop().getItems().isEmpty()) { 373 | player.sendMessage(ChatColor.RED + "This Shop has no Items!"); 374 | return true; 375 | } 376 | mob.getShop().clear(); 377 | this.plugin.saveMobsFile(); 378 | player.sendMessage(ChatColor.GREEN + "Shop cleared!"); 379 | return true; 380 | } else if (args[1].equalsIgnoreCase("items")) { 381 | if (args.length != 2) return false; 382 | if (!mob.haveShop()) { 383 | player.sendMessage(ChatColor.RED + "This Mob haven't a Shop!"); 384 | return true; 385 | } 386 | if (mob.getShop().getItems().isEmpty()) { 387 | player.sendMessage(ChatColor.RED + "This Shop has no Items!"); 388 | return true; 389 | } 390 | player.sendMessage(ChatColor.GOLD + "Items (" + mob.getShop().getItems().size() + ")"); 391 | for (int i = 0; i < mob.getShop().getItems().size(); i++) { 392 | MerchantOffer offer = mob.getShop().getItems().get(i); 393 | StringBuilder builder = new StringBuilder(); 394 | builder.append(ChatColor.GRAY); 395 | builder.append(i); 396 | builder.append(". "); 397 | builder.append(ChatColor.WHITE); 398 | //Item 1 399 | builder.append("Item 1: "); 400 | builder.append(offer.getFirstInput().getType().name()); 401 | builder.append(" ("); 402 | builder.append(offer.getFirstInput().getAmount()); 403 | builder.append(")"); 404 | //Item 2 405 | builder.append(", Item 2: "); 406 | if (offer.getSecondInput() != null) { 407 | builder.append(offer.getSecondInput().getType().name()); 408 | builder.append(" ("); 409 | builder.append(offer.getSecondInput().getAmount()); 410 | builder.append(")"); 411 | } else 412 | builder.append("none"); 413 | //Output 414 | builder.append(", Output: "); 415 | builder.append(offer.getOutput().getType().name()); 416 | builder.append(" ("); 417 | builder.append(offer.getOutput().getAmount()); 418 | builder.append(")"); 419 | 420 | player.sendMessage(builder.toString()); 421 | } 422 | return true; 423 | } else 424 | return false; 425 | } else if (args[0].equalsIgnoreCase("interact")) { 426 | if (args.length < 2) 427 | return false; 428 | if (!player.hasPermission("FakeMobs.interact")) { 429 | player.sendMessage(ChatColor.RED + "No permission!"); 430 | return true; 431 | } 432 | FakeMob mob = Cache.selectedMobs.get(player); 433 | if (mob == null) { 434 | player.sendMessage(ChatColor.RED + "You haven't a Selection!"); 435 | return true; 436 | } 437 | if (args[1].equalsIgnoreCase("add")) { 438 | if (args.length < 4) 439 | return false; 440 | InteractType type = InteractType.getByName(args[2]); 441 | if (type == null) { 442 | player.sendMessage(ChatColor.RED + "Interact type " + args[2] + " can't found!"); 443 | player.sendMessage(ChatColor.GREEN + "Available Types: " + ChatColor.WHITE + InteractType.getStringList()); 444 | return true; 445 | } 446 | InteractAction action; 447 | try { 448 | action = type.getActionClass().newInstance(); 449 | } catch (Exception e) { 450 | e.printStackTrace(); 451 | return true; 452 | } 453 | if (action.getArgsLength() != -1 && (args.length - 3) != action.getArgsLength()) { 454 | player.sendMessage(action.getUsageText()); 455 | } 456 | 457 | StringBuilder argsBuilder = new StringBuilder(); 458 | for (int i = 3; i < args.length; i++) { 459 | if (i != 3) argsBuilder.append(" "); 460 | argsBuilder.append(args[i]); 461 | } 462 | 463 | action.onSet(player, argsBuilder.toString()); 464 | mob.addInteractAction(action); 465 | this.plugin.saveMobsFile(); 466 | return true; 467 | } else if (args[1].equalsIgnoreCase("remove")) { 468 | if (args.length != 3) 469 | return false; 470 | int id; 471 | try { 472 | id = Integer.parseInt(args[2]); 473 | } catch (Exception e) { 474 | player.sendMessage(ChatColor.RED + args[2] + " isn't a valid ID!"); 475 | return true; 476 | } 477 | InteractAction action = mob.getInteractActions().get(id); 478 | if (action == null) { 479 | player.sendMessage(ChatColor.RED + "The Mob haven't a Mob Action with ID #" + id + "!"); 480 | return true; 481 | } 482 | 483 | mob.removeInteractAction(action); 484 | player.sendMessage(ChatColor.GOLD + "Mob Action with ID #" + id + " removed!"); 485 | this.plugin.saveMobsFile(); 486 | return true; 487 | } else if (args[1].equalsIgnoreCase("list")) { 488 | if (args.length != 2) 489 | return false; 490 | if (mob.getInteractActions().isEmpty()) { 491 | player.sendMessage(ChatColor.GOLD + "Registered Mob Actions: " + ChatColor.WHITE + "None"); 492 | } else { 493 | player.sendMessage(ChatColor.GOLD + "Registered Mob Actions:"); 494 | for (int i = 0; i < mob.getInteractActions().size(); i++) { 495 | InteractAction action = mob.getInteractActions().get(i); 496 | StringBuilder builder = new StringBuilder(); 497 | 498 | builder.append(ChatColor.GRAY).append("#").append(i).append(": ") 499 | .append(ChatColor.GOLD).append("Type: ").append(ChatColor.WHITE).append(action.getType().name()) 500 | .append(", ") 501 | .append(ChatColor.GOLD).append("Value: ").append(ChatColor.WHITE).append(action.toString()); 502 | 503 | player.sendMessage(builder.toString()); 504 | } 505 | } 506 | player.sendMessage(ChatColor.GOLD + "Available Interact Actions: " + ChatColor.WHITE + InteractType.getStringList()); 507 | return true; 508 | } else if (args[1].equalsIgnoreCase("clear")) { 509 | if (args.length != 2) 510 | return false; 511 | if (mob.getInteractActions().isEmpty()) { 512 | player.sendMessage(ChatColor.RED + "The Mob haven't Interact Actions!"); 513 | return true; 514 | } 515 | mob.clearInteractAction(); 516 | this.plugin.saveMobsFile(); 517 | player.sendMessage(ChatColor.GOLD + "All Interact Actions removed!"); 518 | return true; 519 | } else 520 | return false; 521 | } else if (args[0].equalsIgnoreCase("remove")) { 522 | if (args.length != 1) return false; 523 | if (!player.hasPermission("FakeMobs.remove")) { 524 | player.sendMessage(ChatColor.RED + "No permission!"); 525 | return true; 526 | } 527 | FakeMob mob = Cache.selectedMobs.get(player); 528 | if (mob == null) { 529 | player.sendMessage(ChatColor.RED + "You haven't a Selection!"); 530 | return true; 531 | } 532 | this.plugin.removeMob(mob.getId()); 533 | player.sendMessage(ChatColor.GREEN + "Mob " + ChatColor.GRAY + "#" + mob.getId() + ChatColor.GREEN + " removed!"); 534 | return true; 535 | } else if (args[0].equalsIgnoreCase("help")) { 536 | if (args.length != 1) return false; 537 | if (!player.hasPermission("FakeMobs.help")) { 538 | player.sendMessage(ChatColor.RED + "No permission!"); 539 | return true; 540 | } 541 | player.sendMessage(ChatColor.GOLD + "Help for " + ChatColor.GRAY + "/FakeMob"); 542 | player.sendMessage(ChatColor.GRAY + "/FakeMob create " + ChatColor.RED + "-- " + ChatColor.WHITE + "Spawn a Fakemob"); 543 | player.sendMessage(ChatColor.GRAY + "/FakeMob select [id] " + ChatColor.RED + "-- " + ChatColor.WHITE + "Select a Fakemob"); 544 | player.sendMessage(ChatColor.GRAY + "/FakeMob name " + ChatColor.RED + "-- " + ChatColor.WHITE + "Give the Fakemob a name"); 545 | player.sendMessage(ChatColor.GRAY + "/FakeMob sitting " + ChatColor.RED + "-- " + ChatColor.WHITE + "Change the Sitting state of a pet and players (Wolf/Ocelot/Player)"); 546 | player.sendMessage(ChatColor.GRAY + "/FakeMob invisibility " + ChatColor.RED + "-- " + ChatColor.WHITE + "Make the fakemob invisibility"); 547 | player.sendMessage(ChatColor.GRAY + "/FakeMob look " + ChatColor.RED + "-- " + ChatColor.WHITE + "Enable/Disable the Players Look"); 548 | player.sendMessage(ChatColor.GRAY + "/FakeMob teleport " + ChatColor.RED + "-- " + ChatColor.WHITE + "Teleport a Fakemob to you"); 549 | player.sendMessage(ChatColor.GRAY + "/FakeMob inv " + ChatColor.RED + "-- " + ChatColor.WHITE + "Set the Inventory of a Fakemob. Use none to delete."); 550 | player.sendMessage(ChatColor.GRAY + "/FakeMob shop enable " + ChatColor.RED + "-- " + ChatColor.WHITE + "Enable the Shop"); 551 | player.sendMessage(ChatColor.GRAY + "/FakeMob shop disable " + ChatColor.RED + "-- " + ChatColor.WHITE + "Disable the Shop"); 552 | player.sendMessage(ChatColor.GRAY + "/FakeMob shop addItem ;[Item 2]; " + ChatColor.RED + "-- " + ChatColor.WHITE + "Add a Item to the Shop"); 553 | player.sendMessage(ChatColor.GRAY + "/FakeMob shop removeItem " + ChatColor.RED + "-- " + ChatColor.WHITE + "Remove a Item with this Id (you can see Id's in /Fakemob shop items"); 554 | player.sendMessage(ChatColor.GRAY + "/FakeMob shop clear " + ChatColor.RED + "-- " + ChatColor.WHITE + "Remove all Items from the Shop"); 555 | player.sendMessage(ChatColor.GRAY + "/FakeMob shop items " + ChatColor.RED + "-- " + ChatColor.WHITE + "Display all Items in the Shop"); 556 | player.sendMessage(ChatColor.GRAY + "/FakeMob interact add " + ChatColor.RED + "-- " + ChatColor.WHITE + "Add Interact Action to the Mob"); 557 | player.sendMessage(ChatColor.GRAY + "/FakeMob interact remove " + ChatColor.RED + "-- " + ChatColor.WHITE + "Remove Interact Action"); 558 | player.sendMessage(ChatColor.GRAY + "/FakeMob interact list " + ChatColor.RED + "-- " + ChatColor.WHITE + "List all Interact Actions from the Mob"); 559 | player.sendMessage(ChatColor.GRAY + "/FakeMob interact clear " + ChatColor.RED + "-- " + ChatColor.WHITE + "Remove all Interact Actions from the Mob"); 560 | player.sendMessage(ChatColor.GRAY + "/FakeMob skin " + ChatColor.RED + "-- " + ChatColor.WHITE + "Set the skin for a player fakemob"); 561 | player.sendMessage(ChatColor.GRAY + "/FakeMob remove " + ChatColor.RED + "-- " + ChatColor.WHITE + "Remove a Fakemob"); 562 | return true; 563 | } else 564 | return false; 565 | } 566 | 567 | } 568 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/event/PlayerInteractFakeMobEvent.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.event; 2 | 3 | import de.howaner.FakeMobs.util.FakeMob; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.event.Event; 6 | import org.bukkit.event.HandlerList; 7 | 8 | public class PlayerInteractFakeMobEvent extends Event { 9 | private final Player player; 10 | private final FakeMob mob; 11 | private final Action action; 12 | private static HandlerList handlers = new HandlerList(); 13 | 14 | public PlayerInteractFakeMobEvent(Player player, FakeMob mob, Action action) { 15 | this.player = player; 16 | this.mob = mob; 17 | this.action = action; 18 | } 19 | 20 | public Player getPlayer() { 21 | return this.player; 22 | } 23 | 24 | public FakeMob getMob() { 25 | return this.mob; 26 | } 27 | 28 | public Action getAction() { 29 | return this.action; 30 | } 31 | 32 | @Override 33 | public HandlerList getHandlers() { 34 | return handlers; 35 | } 36 | 37 | public static HandlerList getHandlerList() { 38 | return handlers; 39 | } 40 | 41 | public static enum Action { 42 | LEFT_CLICK,RIGHT_CLICK; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/event/RemoveFakeMobEvent.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.event; 2 | 3 | import de.howaner.FakeMobs.util.FakeMob; 4 | import org.bukkit.event.Event; 5 | import org.bukkit.event.HandlerList; 6 | 7 | public class RemoveFakeMobEvent extends Event { 8 | private final FakeMob mob; 9 | private static HandlerList handlers = new HandlerList(); 10 | 11 | public RemoveFakeMobEvent(FakeMob mob) { 12 | this.mob = mob; 13 | } 14 | 15 | public FakeMob getMob() { 16 | return this.mob; 17 | } 18 | 19 | @Override 20 | public HandlerList getHandlers() { 21 | return handlers; 22 | } 23 | 24 | public static HandlerList getHandlerList() { 25 | return handlers; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/event/SpawnFakeMobEvent.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.event; 2 | 3 | import de.howaner.FakeMobs.util.FakeMob; 4 | import org.bukkit.Location; 5 | import org.bukkit.entity.EntityType; 6 | import org.bukkit.event.Cancellable; 7 | import org.bukkit.event.Event; 8 | import org.bukkit.event.HandlerList; 9 | 10 | public class SpawnFakeMobEvent extends Event implements Cancellable { 11 | private final Location loc; 12 | private final EntityType type; 13 | private final FakeMob mob; 14 | private boolean cancelled = false; 15 | private static HandlerList handlers = new HandlerList(); 16 | 17 | public SpawnFakeMobEvent(Location loc, EntityType type, FakeMob mob) { 18 | this.loc = loc; 19 | this.type = type; 20 | this.mob = mob; 21 | } 22 | 23 | public Location getLocation() { 24 | return this.loc; 25 | } 26 | 27 | public EntityType getType() { 28 | return this.type; 29 | } 30 | 31 | public FakeMob getMob() { 32 | return this.mob; 33 | } 34 | 35 | @Override 36 | public HandlerList getHandlers() { 37 | return handlers; 38 | } 39 | 40 | public static HandlerList getHandlerList() { 41 | return handlers; 42 | } 43 | 44 | @Override 45 | public boolean isCancelled() { 46 | return this.cancelled; 47 | } 48 | 49 | @Override 50 | public void setCancelled(boolean cancelled) { 51 | this.cancelled = cancelled; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/interact/InteractAction.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.interact; 2 | 3 | import de.howaner.FakeMobs.util.FakeMob; 4 | import org.bukkit.configuration.ConfigurationSection; 5 | import org.bukkit.entity.Player; 6 | 7 | public interface InteractAction { 8 | 9 | public InteractType getType(); 10 | 11 | public int getArgsLength(); 12 | 13 | public String getUsageText(); 14 | 15 | public void onSet(Player player, String value); 16 | 17 | public void onInteract(Player player, FakeMob mob); 18 | 19 | @Override 20 | public String toString(); 21 | 22 | public void loadFromConfig(ConfigurationSection section); 23 | 24 | public void saveToConfig(ConfigurationSection section); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/interact/InteractCommand.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.interact; 2 | 3 | import de.howaner.FakeMobs.util.FakeMob; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.configuration.ConfigurationSection; 8 | import org.bukkit.entity.Player; 9 | 10 | public class InteractCommand implements InteractAction { 11 | private String command; 12 | 13 | @Override 14 | public InteractType getType() { 15 | return InteractType.COMMAND; 16 | } 17 | 18 | @Override 19 | public int getArgsLength() { 20 | return -1; 21 | } 22 | 23 | @Override 24 | public String getUsageText() { 25 | return ChatColor.RED + "Usage: /fakemob interact add command "; 26 | } 27 | 28 | @Override 29 | public void onSet(Player player, String value) { 30 | this.command = value; 31 | if (value.startsWith("s:")) 32 | player.sendMessage(ChatColor.GOLD + "Added Server Command Interact, Command: /" + value.substring(2, value.length())); 33 | else 34 | player.sendMessage(ChatColor.GOLD + "Added Command Interact, Command: /" + value); 35 | } 36 | 37 | @Override 38 | public void onInteract(Player player, FakeMob mob) { 39 | String cmd = this.command; 40 | cmd = cmd.replace("@p", player.getName()); 41 | 42 | CommandSender sender = player; 43 | if (cmd.startsWith("s:")) { 44 | cmd = cmd.substring(2, cmd.length()); 45 | sender = Bukkit.getConsoleSender(); 46 | } 47 | 48 | Bukkit.dispatchCommand(sender, cmd); 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return this.command; 54 | } 55 | 56 | @Override 57 | public void loadFromConfig(ConfigurationSection section) { 58 | this.command = section.getString("Command"); 59 | } 60 | 61 | @Override 62 | public void saveToConfig(ConfigurationSection section) { 63 | section.set("Command", this.command); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/interact/InteractExp.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.interact; 2 | 3 | import de.howaner.FakeMobs.util.FakeMob; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.configuration.ConfigurationSection; 6 | import org.bukkit.entity.Player; 7 | 8 | public class InteractExp implements InteractAction { 9 | private float exp; 10 | 11 | @Override 12 | public InteractType getType() { 13 | return InteractType.EXP; 14 | } 15 | 16 | @Override 17 | public int getArgsLength() { 18 | return 1; 19 | } 20 | 21 | @Override 22 | public String getUsageText() { 23 | return ChatColor.RED + "Usage: /fakemob interact add exp "; 24 | } 25 | 26 | @Override 27 | public void onSet(Player player, String value) { 28 | try { 29 | this.exp = Float.parseFloat(value); 30 | player.sendMessage("Added Exp Interact, Exp: " + this.exp); 31 | } catch (Exception e) { 32 | player.sendMessage(ChatColor.RED + value + " isn't a valid Exp amount!"); 33 | } 34 | } 35 | 36 | @Override 37 | public void onInteract(Player player, FakeMob mob) { 38 | player.setExp(player.getExp() + this.exp); 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return String.valueOf(this.exp); 44 | } 45 | 46 | @Override 47 | public void loadFromConfig(ConfigurationSection section) { 48 | this.exp = Float.parseFloat(section.getString("Exp")); 49 | } 50 | 51 | @Override 52 | public void saveToConfig(ConfigurationSection section) { 53 | section.set("Exp", String.valueOf(this.exp)); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/interact/InteractItem.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.interact; 2 | 3 | import de.howaner.FakeMobs.util.FakeMob; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.Material; 6 | import org.bukkit.configuration.ConfigurationSection; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.inventory.ItemStack; 9 | 10 | public class InteractItem implements InteractAction { 11 | private ItemStack item; 12 | 13 | @Override 14 | public InteractType getType() { 15 | return InteractType.ITEM; 16 | } 17 | 18 | @Override 19 | public int getArgsLength() { 20 | return 1; 21 | } 22 | 23 | @Override 24 | public String getUsageText() { 25 | return ChatColor.RED + "Usage: /fakemob interact add item "; 26 | } 27 | 28 | @Override 29 | public void onSet(Player player, String value) { 30 | short data = 0; 31 | int amount = 1; 32 | 33 | if (value.contains(";")) { 34 | try { 35 | amount = Integer.parseInt(value.split(";")[1]); 36 | value = value.split(";")[0]; 37 | } catch (Exception e) { 38 | player.sendMessage(ChatColor.RED + value.split(";")[1] + " isn't a valid amount!"); 39 | return; 40 | } 41 | } 42 | if (value.contains(":")) { 43 | try { 44 | data = Short.parseShort(value.split(":")[1]); 45 | value = value.split(":")[0]; 46 | } catch (Exception e) { 47 | player.sendMessage(ChatColor.RED + value.split(":")[1] + " isn't a valid item data!"); 48 | return; 49 | } 50 | } 51 | 52 | Material mat = Material.matchMaterial(value); 53 | if (mat == null) { 54 | player.sendMessage(ChatColor.RED + "Item " + value + " not exists!"); 55 | return; 56 | } 57 | 58 | this.item = new ItemStack(mat, amount, data); 59 | player.sendMessage(ChatColor.GOLD + "Added Item Interact, Item: " + this.toString()); 60 | } 61 | 62 | @Override 63 | public void onInteract(Player player, FakeMob mob) { 64 | player.getInventory().addItem(this.item.clone()); 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | StringBuilder builder = new StringBuilder(); 70 | builder.append(this.item.getType().name()); 71 | if (this.item.getData().getData() != (byte)0) 72 | builder.append(":").append(this.item.getData().getData()); 73 | if (this.item.getAmount() != 1) 74 | builder.append(";").append(this.item.getAmount()); 75 | 76 | return builder.toString(); 77 | } 78 | 79 | @Override 80 | public void loadFromConfig(ConfigurationSection section) { 81 | this.item = section.getItemStack("Item"); 82 | } 83 | 84 | @Override 85 | public void saveToConfig(ConfigurationSection section) { 86 | section.set("Item", this.item); 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/interact/InteractText.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.interact; 2 | 3 | import de.howaner.FakeMobs.util.FakeMob; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.configuration.ConfigurationSection; 6 | import org.bukkit.entity.Player; 7 | 8 | public class InteractText implements InteractAction { 9 | private String text; 10 | 11 | @Override 12 | public InteractType getType() { 13 | return InteractType.TEXT; 14 | } 15 | 16 | @Override 17 | public int getArgsLength() { 18 | return -1; 19 | } 20 | 21 | @Override 22 | public String getUsageText() { 23 | return ChatColor.RED + "Usage: /fakemob interact add text "; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return this.text; 29 | } 30 | 31 | @Override 32 | public void onSet(Player player, String value) { 33 | this.text = value; 34 | player.sendMessage(ChatColor.GOLD + "Added Text Interact, Text: " + value); 35 | } 36 | 37 | @Override 38 | public void onInteract(Player player, FakeMob mob) { 39 | player.sendMessage(ChatColor.translateAlternateColorCodes('&', this.text)); 40 | } 41 | 42 | @Override 43 | public void loadFromConfig(ConfigurationSection section) { 44 | this.text = section.getString("Text"); 45 | } 46 | 47 | @Override 48 | public void saveToConfig(ConfigurationSection section) { 49 | section.set("Text", this.text); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/interact/InteractType.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.interact; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public enum InteractType { 7 | COMMAND,TEXT,ITEM,EXP; 8 | 9 | public static final Map> actions = new HashMap>(); 10 | 11 | static { 12 | actions.put(InteractType.COMMAND, InteractCommand.class); 13 | actions.put(InteractType.TEXT, InteractText.class); 14 | actions.put(InteractType.ITEM, InteractItem.class); 15 | actions.put(InteractType.EXP, InteractExp.class); 16 | } 17 | 18 | public Class getActionClass() { 19 | return actions.get(this); 20 | } 21 | 22 | public static String getStringList() { 23 | StringBuilder builder = new StringBuilder(); 24 | for (int i = 0; i < values().length; i++) { 25 | if (i != 0) builder.append(", "); 26 | builder.append(values()[i]); 27 | } 28 | return builder.toString(); 29 | } 30 | 31 | public static InteractType getByName(String name) { 32 | try { 33 | return valueOf(name.toUpperCase()); 34 | } catch (Exception e) { 35 | return null; 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/listener/InteractListener.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.listener; 2 | 3 | import de.howaner.FakeMobs.event.PlayerInteractFakeMobEvent; 4 | import de.howaner.FakeMobs.event.PlayerInteractFakeMobEvent.Action; 5 | import de.howaner.FakeMobs.interact.InteractAction; 6 | import de.howaner.FakeMobs.util.FakeMob; 7 | import org.bukkit.entity.Player; 8 | import org.bukkit.event.EventHandler; 9 | import org.bukkit.event.Listener; 10 | 11 | public class InteractListener implements Listener { 12 | 13 | @EventHandler 14 | public void onInteractFakeMobEvent(PlayerInteractFakeMobEvent event) { 15 | if (event.getAction() != Action.RIGHT_CLICK) return; 16 | Player player = event.getPlayer(); 17 | FakeMob mob = event.getMob(); 18 | 19 | for (InteractAction action : mob.getInteractActions()) 20 | action.onInteract(player, mob); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/listener/MobListener.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.listener; 2 | 3 | import de.howaner.FakeMobs.FakeMobsPlugin; 4 | import de.howaner.FakeMobs.adjuster.MyWorldAccess; 5 | import de.howaner.FakeMobs.event.PlayerInteractFakeMobEvent; 6 | import de.howaner.FakeMobs.event.PlayerInteractFakeMobEvent.Action; 7 | import de.howaner.FakeMobs.util.Cache; 8 | import de.howaner.FakeMobs.util.FakeMob; 9 | import org.bukkit.Bukkit; 10 | import org.bukkit.ChatColor; 11 | import org.bukkit.Chunk; 12 | import org.bukkit.Location; 13 | import org.bukkit.entity.Player; 14 | import org.bukkit.event.EventHandler; 15 | import org.bukkit.event.EventPriority; 16 | import org.bukkit.event.Listener; 17 | import org.bukkit.event.block.BlockPlaceEvent; 18 | import org.bukkit.event.entity.PlayerDeathEvent; 19 | import org.bukkit.event.player.PlayerChangedWorldEvent; 20 | import org.bukkit.event.player.PlayerJoinEvent; 21 | import org.bukkit.event.player.PlayerMoveEvent; 22 | import org.bukkit.event.player.PlayerQuitEvent; 23 | import org.bukkit.event.player.PlayerRespawnEvent; 24 | import org.bukkit.event.player.PlayerTeleportEvent; 25 | import org.bukkit.event.world.WorldInitEvent; 26 | import org.bukkit.event.world.WorldUnloadEvent; 27 | 28 | public class MobListener implements Listener { 29 | private FakeMobsPlugin plugin; 30 | 31 | public MobListener(FakeMobsPlugin plugin) { 32 | this.plugin = plugin; 33 | } 34 | 35 | @EventHandler (priority = EventPriority.HIGHEST) 36 | public void onWorldInit(WorldInitEvent event) { 37 | MyWorldAccess.registerWorldAccess(event.getWorld()); 38 | } 39 | 40 | @EventHandler (priority = EventPriority.HIGHEST) 41 | public void onWorldUnload(WorldUnloadEvent event) { 42 | MyWorldAccess.unregisterWorldAccess(event.getWorld()); 43 | } 44 | 45 | @EventHandler 46 | public void onSelectMob(PlayerInteractFakeMobEvent event) { 47 | Player player = event.getPlayer(); 48 | FakeMob mob = event.getMob(); 49 | if (Cache.selectedMobs.containsKey(player) && Cache.selectedMobs.get(player) == null) { 50 | Cache.selectedMobs.put(player, mob); 51 | player.sendMessage(ChatColor.GREEN + "Mob " + ChatColor.GRAY + "#" + mob.getId() + ChatColor.GREEN + " selected!"); 52 | return; 53 | } 54 | 55 | if (event.getAction() == Action.RIGHT_CLICK && mob.haveShop()) 56 | mob.getShop().openShop(player, (mob.getCustomName() != null && !mob.getCustomName().isEmpty()) ? mob.getCustomName() : null); 57 | } 58 | 59 | @EventHandler 60 | public void onPlayerTeleport(PlayerTeleportEvent event) { 61 | final Player player = event.getPlayer(); 62 | 63 | Bukkit.getScheduler().runTaskLater(FakeMobsPlugin.getPlugin(), new Runnable() { 64 | @Override 65 | public void run() { 66 | MobListener.this.plugin.updatePlayerView(player); 67 | } 68 | }, 5L); 69 | } 70 | 71 | @EventHandler 72 | public void onPlayerMove(PlayerMoveEvent event) { 73 | if (event.getFrom().getWorld() == event.getTo().getWorld() && 74 | event.getFrom().getBlockX() == event.getTo().getBlockX() && 75 | event.getFrom().getBlockY() == event.getTo().getBlockY() && 76 | event.getFrom().getBlockZ() == event.getTo().getBlockZ()) 77 | return; 78 | 79 | Player player = event.getPlayer(); 80 | if (player.getHealth() <= 0.0D) return; // player is dead 81 | 82 | Chunk oldChunk = event.getFrom().getChunk(); 83 | Chunk newChunk = event.getTo().getChunk(); 84 | 85 | // Only when the player moves a complete chunk: 86 | if (oldChunk.getWorld() != newChunk.getWorld() || oldChunk.getX() != newChunk.getX() || oldChunk.getZ() != newChunk.getZ()) { 87 | this.plugin.updatePlayerView(player); 88 | } 89 | } 90 | 91 | @EventHandler 92 | public void onPlayerJoin(PlayerJoinEvent event) { 93 | Player player = event.getPlayer(); 94 | this.plugin.updatePlayerView(player); 95 | } 96 | 97 | @EventHandler 98 | public void onPlayerChangedWorld(PlayerChangedWorldEvent event) { 99 | Player player = event.getPlayer(); 100 | this.plugin.updatePlayerView(player); 101 | } 102 | 103 | @EventHandler 104 | public void onBlockPlace(BlockPlaceEvent event) { 105 | Location loc = event.getBlock().getLocation(); 106 | if (this.plugin.isMobOnLocation(loc)) 107 | event.setCancelled(true); 108 | } 109 | 110 | @EventHandler 111 | public void onPlayerQuit(PlayerQuitEvent event) { 112 | Player player = event.getPlayer(); 113 | if (Cache.selectedMobs.containsKey(player)) 114 | Cache.selectedMobs.remove(player); 115 | for (FakeMob mob : this.plugin.getMobs()) 116 | mob.unloadPlayer(player); 117 | } 118 | 119 | @EventHandler 120 | public void onPlayerRespawn(PlayerRespawnEvent event) { 121 | final Player player = event.getPlayer(); 122 | 123 | Bukkit.getScheduler().runTaskLater(FakeMobsPlugin.getPlugin(), new Runnable() { 124 | @Override 125 | public void run() { 126 | MobListener.this.plugin.updatePlayerView(player); 127 | } 128 | }, 5L); 129 | } 130 | 131 | @EventHandler 132 | public void onPlayerDeath(PlayerDeathEvent event) { 133 | Player player = event.getEntity(); 134 | 135 | for (FakeMob mob : this.plugin.getMobs()) 136 | mob.unloadPlayer(player); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/listener/ProtocolListener.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.listener; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import com.comphenix.protocol.events.ListenerOptions; 5 | import com.comphenix.protocol.events.ListenerPriority; 6 | import com.comphenix.protocol.events.ListeningWhitelist; 7 | import com.comphenix.protocol.events.PacketContainer; 8 | import com.comphenix.protocol.events.PacketEvent; 9 | import com.comphenix.protocol.events.PacketListener; 10 | import com.comphenix.protocol.injector.GamePhase; 11 | import de.howaner.FakeMobs.FakeMobsPlugin; 12 | import de.howaner.FakeMobs.event.PlayerInteractFakeMobEvent; 13 | import de.howaner.FakeMobs.event.PlayerInteractFakeMobEvent.Action; 14 | import de.howaner.FakeMobs.util.FakeMob; 15 | import java.lang.reflect.Field; 16 | import org.bukkit.Bukkit; 17 | import org.bukkit.entity.Player; 18 | import org.bukkit.plugin.Plugin; 19 | 20 | public class ProtocolListener implements PacketListener { 21 | private FakeMobsPlugin plugin; 22 | 23 | public ProtocolListener(FakeMobsPlugin plugin) { 24 | this.plugin = plugin; 25 | } 26 | 27 | @Override 28 | public void onPacketSending(PacketEvent pe) { } 29 | 30 | @Override 31 | public void onPacketReceiving(PacketEvent pe) { 32 | PacketContainer packet = pe.getPacket(); 33 | final Player player = pe.getPlayer(); 34 | 35 | if (packet.getType() == PacketType.Play.Client.USE_ENTITY) { 36 | int id = packet.getIntegers().read(0) - 2300; 37 | 38 | if (id < 0) return; 39 | final FakeMob mob = this.plugin.getMob(id); 40 | if (mob == null || player.getWorld() != mob.getWorld()) return; 41 | 42 | if (player.isDead()) return; 43 | if (player.getLocation().distance(mob.getLocation()) > 8) { 44 | return; 45 | } 46 | 47 | final Action action; 48 | try { 49 | Field field = packet.getEntityUseActions().getField(0); 50 | field.setAccessible(true); 51 | Object obj = field.get(packet.getEntityUseActions().getTarget()); 52 | String actionName = (obj == null) ? "" : obj.toString(); 53 | 54 | if (actionName.equals("INTERACT")) { 55 | action = Action.RIGHT_CLICK; 56 | } else if (actionName.equals("ATTACK")) { 57 | action = Action.LEFT_CLICK; 58 | } else { 59 | return; 60 | } 61 | } catch (Exception e) { 62 | e.printStackTrace(); 63 | return; 64 | } 65 | 66 | pe.setCancelled(true); 67 | Bukkit.getScheduler().runTask(this.plugin, new Runnable() { 68 | @Override 69 | public void run() { 70 | PlayerInteractFakeMobEvent event = new PlayerInteractFakeMobEvent(player, mob, action); 71 | Bukkit.getPluginManager().callEvent(event); 72 | } 73 | }); 74 | } 75 | } 76 | 77 | @Override 78 | public ListeningWhitelist getSendingWhitelist() { 79 | return ListeningWhitelist.EMPTY_WHITELIST; 80 | } 81 | 82 | @Override 83 | public ListeningWhitelist getReceivingWhitelist() { 84 | return ListeningWhitelist.newBuilder(). 85 | priority(ListenerPriority.NORMAL). 86 | types(PacketType.Play.Client.USE_ENTITY). 87 | gamePhase(GamePhase.PLAYING). 88 | options(new ListenerOptions[0]). 89 | build(); 90 | } 91 | 92 | @Override 93 | public Plugin getPlugin() { 94 | return this.plugin; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/merchant/Merchant.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.merchant; 2 | 3 | import de.howaner.FakeMobs.merchant.ReflectionUtils.NMSMerchantRecipe; 4 | import java.lang.reflect.Proxy; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.entity.Player; 9 | 10 | public class Merchant { 11 | private NMSMerchant h; 12 | 13 | public Merchant() { 14 | this.h = new NMSMerchant(); 15 | this.h.proxy = Proxy.newProxyInstance(Bukkit.class.getClassLoader(), new Class[] { ReflectionUtils.getClassByName(ReflectionUtils.getNMSPackageName() + ".IMerchant") }, this.h); 16 | } 17 | 18 | public String getTitle() { 19 | return this.h.title; 20 | } 21 | 22 | public void setTitle(String title) { 23 | this.h.title = title; 24 | } 25 | 26 | public List getOffers() { 27 | List offerList = new ArrayList(); 28 | for (Object recipe : (List) this.h.getOffers(null)) { 29 | offerList.add(new MerchantOffer(new NMSMerchantRecipe(recipe))); 30 | } 31 | return offerList; 32 | } 33 | 34 | public Merchant addOffer(MerchantOffer offer) { 35 | this.h.a(offer.getHandle().getMerchantRecipe()); 36 | return this; 37 | } 38 | 39 | public Merchant addOffers(MerchantOffer[] offers) { 40 | for (MerchantOffer o : offers) { 41 | addOffer(o); 42 | } 43 | return this; 44 | } 45 | 46 | public Merchant setOffers(List offers) { 47 | this.h.clearRecipes(); 48 | for (MerchantOffer o : offers) 49 | this.addOffer(o); 50 | return this; 51 | } 52 | 53 | public boolean hasCustomer() { 54 | return this.h.getEntityHuman() != null; 55 | } 56 | 57 | public Player getCustomer() { 58 | return (Player)(this.h.getEntityHuman() == null ? null : this.h.getBukkitEntity()); 59 | } 60 | 61 | public Merchant setCustomer(Player player) { 62 | this.h.a_(player == null ? null : ReflectionUtils.toEntityHuman(player)); 63 | return this; 64 | } 65 | 66 | public void openTrading(Player player) { 67 | this.h.openTrading(ReflectionUtils.toEntityHuman(player)); 68 | } 69 | 70 | protected NMSMerchant getHandle() { 71 | return this.h; 72 | } 73 | 74 | @Override 75 | public Merchant clone() { 76 | Merchant clone = new Merchant().setOffers(getOffers()).setCustomer(getCustomer()); 77 | clone.setTitle(this.getTitle()); 78 | return clone; 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/merchant/MerchantOffer.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.merchant; 2 | 3 | import de.howaner.FakeMobs.merchant.ReflectionUtils.NMSMerchantRecipe; 4 | import de.howaner.FakeMobs.merchant.ReflectionUtils.OBCCraftItemStack; 5 | import org.bukkit.inventory.ItemStack; 6 | 7 | public class MerchantOffer { 8 | private ItemStack[] items = new ItemStack[3]; 9 | private int maxUses = Integer.MAX_VALUE; 10 | 11 | public MerchantOffer(ItemStack is1, ItemStack is2, ItemStack re) { 12 | this.items[0] = is1; 13 | this.items[1] = is2; 14 | this.items[2] = re; 15 | } 16 | 17 | public MerchantOffer(ItemStack is, ItemStack re) { 18 | this(is, null, re); 19 | } 20 | 21 | protected MerchantOffer(NMSMerchantRecipe handle) { 22 | this.items[0] = OBCCraftItemStack.asBukkitCopy(handle.getBuyItem1()); 23 | this.items[1] = (handle.getBuyItem2() == null) ? null : OBCCraftItemStack.asBukkitCopy(handle.getBuyItem2()); 24 | this.items[2] = OBCCraftItemStack.asBukkitCopy(handle.getBuyItem3()); 25 | } 26 | 27 | protected NMSMerchantRecipe getHandle() { 28 | NMSMerchantRecipe nms; 29 | if (this.items[1] == null) 30 | nms = new NMSMerchantRecipe(OBCCraftItemStack.asNMSCopy(this.items[0]), OBCCraftItemStack.asNMSCopy(this.items[2])); 31 | else 32 | nms = new NMSMerchantRecipe(OBCCraftItemStack.asNMSCopy(this.items[0]), OBCCraftItemStack.asNMSCopy(this.items[1]), OBCCraftItemStack.asNMSCopy(this.items[2])); 33 | 34 | nms.setMaxUses(this.maxUses); 35 | return nms; 36 | } 37 | 38 | public ItemStack getFirstInput() { 39 | return this.items[0]; 40 | } 41 | 42 | public ItemStack getSecondInput() { 43 | return this.items[1]; 44 | } 45 | 46 | public ItemStack getOutput() { 47 | return this.items[2]; 48 | } 49 | 50 | public int getMaxUses() { 51 | return this.maxUses; 52 | } 53 | 54 | public void setMaxUses(int maxUses) { 55 | this.maxUses = maxUses; 56 | } 57 | 58 | @Override 59 | public MerchantOffer clone() { 60 | MerchantOffer clone = new MerchantOffer(this.getFirstInput(), this.getSecondInput(), this.getOutput()); 61 | clone.setMaxUses(this.getMaxUses()); 62 | return clone; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/merchant/NMSMerchant.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.merchant; 2 | 3 | import de.howaner.FakeMobs.merchant.ReflectionUtils.NMSMerchantRecipe; 4 | import de.howaner.FakeMobs.merchant.ReflectionUtils.NMSMerchantRecipeList; 5 | import java.lang.reflect.Method; 6 | import org.bukkit.entity.Player; 7 | 8 | public class NMSMerchant implements java.lang.reflect.InvocationHandler { 9 | private NMSMerchantRecipeList o = new NMSMerchantRecipeList(); //MerchantRecipeList 10 | private transient Object c; //EntityHuman 11 | public Object proxy; 12 | public String title; 13 | 14 | @Override 15 | public Object invoke(Object proxy, Method m, Object[] args) { 16 | try { 17 | if (m == null || m.getName() == null) return null; 18 | Class entityHuman = ReflectionUtils.getClassByName(ReflectionUtils.getNMSPackageName() + ".EntityHuman"); 19 | if ((m.getName().equals("a_") || m.getName().equals("setTradingPlayer")) && args.length == 1 && args[0] != null && args[0].getClass().isInstance(entityHuman)) // a_ = Spigot 1.8, setTradingPlayer = Spigot 1.13 20 | this.a_(args[0]); 21 | else if (m.getName().equals("b") || m.getName().equals("m_") || m.getName().equals("u_") || m.getName().equals("v_") || m.getName().equals("getTrader")) //m_ = 1.6.4, b = 1.7.4, u_ = Spigot 1.8, v_ = Spigot 1.8.3, getTrader = Spigot 1.13 22 | return this.getEntityHuman(); 23 | else if (m.getName().equals("getOffers") && args.length == 1) 24 | return this.getOffers(args[0]); 25 | else if (m.getName().equals("a") && args.length == 1) 26 | this.a(args[0]); 27 | else if (m.getName().equals("getScoreboardDisplayName")) 28 | return this.getScoreboardDisplayName(); 29 | } catch (Exception e) { 30 | e.printStackTrace(); 31 | } 32 | return null; 33 | } 34 | 35 | public Object getScoreboardDisplayName() { 36 | return ReflectionUtils.createNMSTextComponent(this.title); 37 | } 38 | 39 | public void a_(Object player) { //Class = EntityHuman 40 | this.c = player; 41 | } 42 | 43 | public Object getEntityHuman() { // Return Class = EntityHuman 44 | return this.c; 45 | } 46 | 47 | public Object getOffers(Object player) { //Return Class = MerchantRecipeList, player Class = EntityHuman 48 | return this.o.getHandle(); 49 | } 50 | 51 | public void a(Object recipe) { //recipe Class = MerchantRecipe 52 | this.o.add(new NMSMerchantRecipe(recipe)); 53 | } 54 | 55 | /*public void a_(ItemStack is) { 56 | 57 | }*/ 58 | 59 | /* Other Methods */ 60 | public Player getBukkitEntity() { 61 | try { 62 | Class c = ReflectionUtils.getClassByName(ReflectionUtils.getNMSPackageName() + ".EntityHuman"); 63 | Method m = c.getDeclaredMethod("getBukkitEntity"); 64 | m.setAccessible(true); 65 | return (Player) m.invoke(this.c); 66 | } catch (Exception e) { 67 | e.printStackTrace(); 68 | return null; 69 | } 70 | } 71 | 72 | public void clearRecipes() { 73 | this.o.clear(); 74 | } 75 | 76 | public void setRecipes(NMSMerchantRecipeList recipes) { 77 | this.o = recipes; 78 | } 79 | 80 | public void openTrading(Object player) { //player Class = EntityPlayer 81 | this.c = player; 82 | 83 | try { 84 | Class classs = ReflectionUtils.getClassByName(ReflectionUtils.getNMSPackageName() + ".EntityPlayer"); 85 | Method m; 86 | if (this.getMethodArgs(classs, "openTrade") == 2) { 87 | // Older than Spigot 1.8 (maybe Bukkit 1.7.10) 88 | m = classs.getDeclaredMethod("openTrade", ReflectionUtils.getClassByName(ReflectionUtils.getNMSPackageName() + ".IMerchant"), String.class); 89 | m.setAccessible(true); 90 | m.invoke(player, this.proxy, this.title); 91 | } else { 92 | // Spigot 1.8 and newer 93 | m = classs.getDeclaredMethod("openTrade", ReflectionUtils.getClassByName(ReflectionUtils.getNMSPackageName() + ".IMerchant")); 94 | m.setAccessible(true); 95 | m.invoke(player, this.proxy); 96 | } 97 | } catch (Exception e) { 98 | e.printStackTrace(); 99 | } 100 | } 101 | 102 | private int getMethodArgs(Class classs, String methodName) { 103 | for (Method method : classs.getDeclaredMethods()) { 104 | if (method.getName().equals(methodName)) { 105 | return method.getParameterTypes().length; 106 | } 107 | } 108 | return -1; 109 | } 110 | 111 | } -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/merchant/ReflectionUtils.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.merchant; 2 | 3 | import java.lang.reflect.Constructor; 4 | import java.lang.reflect.Field; 5 | import java.lang.reflect.Method; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.GameMode; 10 | import org.bukkit.entity.Player; 11 | import org.bukkit.inventory.ItemStack; 12 | 13 | public class ReflectionUtils { 14 | 15 | public static class OBCCraftItemStack { 16 | 17 | public static Class getOBCClass() { 18 | return ReflectionUtils.getClassByName(ReflectionUtils.getOBCPackageName() + ".inventory.CraftItemStack"); 19 | } 20 | 21 | public static ItemStack asBukkitCopy(Object nmsItemStack) { 22 | try { 23 | Method m = getOBCClass().getDeclaredMethod("asBukkitCopy", ReflectionUtils.getClassByName(ReflectionUtils.getNMSPackageName() + ".ItemStack")); 24 | m.setAccessible(true); 25 | return (ItemStack) m.invoke(null, nmsItemStack); 26 | } catch (Exception e) { 27 | e.printStackTrace(); 28 | return null; 29 | } 30 | } 31 | 32 | public static Object asNMSCopy(ItemStack stack) { 33 | try { 34 | Method m = getOBCClass().getDeclaredMethod("asNMSCopy", ItemStack.class); 35 | m.setAccessible(true); 36 | return m.invoke(null, stack); 37 | } catch (Exception e) { 38 | e.printStackTrace(); 39 | return null; 40 | } 41 | } 42 | 43 | public static Object getEmptyItemStack() { 44 | return asNMSCopy(null); 45 | } 46 | 47 | } 48 | 49 | public static class NMSMerchantRecipeList { 50 | private Object handle; 51 | 52 | public static Class getNMSClass() { 53 | return ReflectionUtils.getClassByName(ReflectionUtils.getNMSPackageName() + ".MerchantRecipeList"); 54 | } 55 | 56 | public NMSMerchantRecipeList() { 57 | try { 58 | this.handle = getNMSClass().newInstance(); 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | 64 | public NMSMerchantRecipeList(Object handle) { 65 | this.handle = handle; 66 | } 67 | 68 | public Object getHandle() { 69 | return this.handle; 70 | } 71 | 72 | public void clear() { 73 | try { 74 | Method m = ArrayList.class.getDeclaredMethod("clear"); 75 | m.setAccessible(true); 76 | m.invoke(this.handle); 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | 82 | public void add(NMSMerchantRecipe recipe) { 83 | try { 84 | Method m = ArrayList.class.getDeclaredMethod("add", Object.class); 85 | //Method m = getNMSClass().getDeclaredMethod("add", NMSMerchantRecipe.getNMSClass()); 86 | m.setAccessible(true); 87 | m.invoke(this.handle, recipe.getMerchantRecipe()); 88 | } catch (Exception e) { 89 | e.printStackTrace(); 90 | } 91 | } 92 | 93 | public List getRecipes() { 94 | List recipeList = new ArrayList(); 95 | for (Object obj : (List) handle) { 96 | recipeList.add(new NMSMerchantRecipe(obj)); 97 | } 98 | return recipeList; 99 | } 100 | } 101 | 102 | public static class NMSMerchantRecipe { 103 | private Object merchantRecipe; 104 | 105 | public NMSMerchantRecipe(Object merchantRecipe) { 106 | this.merchantRecipe = merchantRecipe; 107 | } 108 | 109 | public NMSMerchantRecipe(Object item1, Object item3) { 110 | this(item1, null, item3); 111 | } 112 | 113 | public NMSMerchantRecipe(Object item1, Object item2, Object item3) { 114 | if (item2 == null) 115 | item2 = OBCCraftItemStack.getEmptyItemStack(); 116 | 117 | try { 118 | Class isClass = ReflectionUtils.getClassByName(ReflectionUtils.getNMSPackageName() + ".ItemStack"); 119 | this.merchantRecipe = getNMSClass().getDeclaredConstructor(isClass, isClass, isClass).newInstance(item1, item2, item3); 120 | } catch (Exception e) { 121 | e.printStackTrace(); 122 | } 123 | } 124 | 125 | public static Class getNMSClass() { 126 | return ReflectionUtils.getClassByName(ReflectionUtils.getNMSPackageName() + ".MerchantRecipe"); 127 | } 128 | 129 | public Object getBuyItem1() { 130 | try { 131 | Method m = getNMSClass().getDeclaredMethod("getBuyItem1"); 132 | m.setAccessible(true); 133 | return m.invoke(this.merchantRecipe); 134 | } catch (Exception e) { 135 | e.printStackTrace(); 136 | return null; 137 | } 138 | } 139 | 140 | public Object getBuyItem2() { 141 | try { 142 | Method m = getNMSClass().getDeclaredMethod("getBuyItem2"); 143 | m.setAccessible(true); 144 | return m.invoke(this.merchantRecipe); 145 | } catch (Exception e) { 146 | e.printStackTrace(); 147 | return null; 148 | } 149 | } 150 | 151 | public Object getBuyItem3() { 152 | try { 153 | Method m = getNMSClass().getDeclaredMethod("getBuyItem3"); 154 | m.setAccessible(true); 155 | return m.invoke(this.merchantRecipe); 156 | } catch (Exception e) { 157 | e.printStackTrace(); 158 | return null; 159 | } 160 | } 161 | 162 | public int getMaxUses() { 163 | try { 164 | Field field = getNMSClass().getDeclaredField("maxUses"); 165 | field.setAccessible(true); 166 | return (int) field.getByte(this.merchantRecipe); 167 | } catch (Exception e) { 168 | e.printStackTrace(); 169 | return 0; 170 | } 171 | } 172 | 173 | public void setMaxUses(int maxUses) { 174 | try { 175 | Field field = getNMSClass().getDeclaredField("maxUses"); 176 | field.setAccessible(true); 177 | field.set(this.merchantRecipe, maxUses); 178 | } catch (Exception e) { 179 | e.printStackTrace(); 180 | } 181 | } 182 | 183 | public Object getMerchantRecipe() { 184 | return this.merchantRecipe; 185 | } 186 | 187 | } 188 | 189 | public static class PlayerInfoAction { 190 | public static Object UPDATE_GAME_MODE = getNMSAction("UPDATE_GAME_MODE"); 191 | public static Object ADD_PLAYER = getNMSAction("ADD_PLAYER"); 192 | public static Object UPDATE_DISPLAY_NAME = getNMSAction("UPDATE_DISPLAY_NAME"); 193 | public static Object REMOVE_PLAYER = getNMSAction("REMOVE_PLAYER"); 194 | private static Class nmsClass; 195 | 196 | // Return: EnumPlayerInfoAction 197 | private static Object getNMSAction(String name) { 198 | try { 199 | Field field = getNMSClass().getDeclaredField(name); 200 | field.setAccessible(true); 201 | return field.get(null); 202 | } catch (Exception ex) { 203 | ex.printStackTrace(); 204 | return null; 205 | } 206 | } 207 | 208 | public static Class getNMSClass() { 209 | if (nmsClass == null) { 210 | nmsClass = getClassByName(getNMSPackageName() + ".PacketPlayOutPlayerInfo$EnumPlayerInfoAction"); // Spigot 1.8.3 211 | if (nmsClass == null) { 212 | // Spigot 1.8.0 213 | nmsClass = getClassByName(getNMSPackageName() + ".EnumPlayerInfoAction"); 214 | } 215 | } 216 | return nmsClass; 217 | } 218 | } 219 | 220 | // Return: EnumGamemode 221 | public static Object createNMSGameMode(GameMode gameMode) { 222 | Class c = getClassByName(getNMSPackageName() + ".EnumGamemode"); // Spigot 1.8.0 223 | if (c == null) { 224 | // Spigot 1.8.3 225 | c = getClassByName(getNMSPackageName() + ".WorldSettings$EnumGamemode"); 226 | } 227 | 228 | try { 229 | Method method = c.getDeclaredMethod("getById", int.class); 230 | method.setAccessible(true); 231 | return method.invoke(null, gameMode.getValue()); 232 | } catch (Exception ex) { 233 | ex.printStackTrace(); 234 | return null; 235 | } 236 | } 237 | 238 | public static Object createPlayerInfoData(Object gameProfile, GameMode gameMode, int ping, String nickName) { 239 | boolean is_1_8_3 = true; 240 | Class playerInfoDataClass = getClassByName(getNMSPackageName() + ".PacketPlayOutPlayerInfo$PlayerInfoData"); 241 | 242 | if (playerInfoDataClass == null) { 243 | // Spigot 1.8 244 | playerInfoDataClass = getClassByName(getNMSPackageName() + ".PlayerInfoData"); 245 | is_1_8_3 = false; 246 | } 247 | 248 | Object nmsGameMode = createNMSGameMode(gameMode); 249 | 250 | try { 251 | Constructor constructor = playerInfoDataClass.getDeclaredConstructor( 252 | getClassByName(getNMSPackageName() + ".PacketPlayOutPlayerInfo"), 253 | getClassByName("com.mojang.authlib.GameProfile"), 254 | int.class, 255 | nmsGameMode.getClass(), 256 | getClassByName(getNMSPackageName() + ".IChatBaseComponent") 257 | ); 258 | constructor.setAccessible(true); 259 | return constructor.newInstance(null, gameProfile, ping, nmsGameMode, createNMSTextComponent(nickName)); 260 | } catch (Exception ex) { 261 | ex.printStackTrace(); 262 | return null; 263 | } 264 | } 265 | 266 | public static Object fillProfileProperties(Object gameProfile) { 267 | Class serverClass = getClassByName(getNMSPackageName() + ".MinecraftServer"); 268 | Class sessionServiceClass = getClassByName("com.mojang.authlib.minecraft.MinecraftSessionService"); 269 | 270 | try { 271 | Object minecraftServer; 272 | { 273 | Method method = serverClass.getDeclaredMethod("getServer"); 274 | method.setAccessible(true); 275 | minecraftServer = method.invoke(null); 276 | } 277 | 278 | Object sessionService; 279 | { 280 | String methodName; 281 | if (existsMethod(serverClass, "aC", sessionServiceClass)) 282 | methodName = "aC"; //1.8.3 283 | else 284 | methodName = "aD"; //1.8.8 285 | Method method = serverClass.getDeclaredMethod(methodName); 286 | method.setAccessible(true); 287 | sessionService = method.invoke(minecraftServer); 288 | } 289 | 290 | Object result; 291 | { 292 | Method method = sessionServiceClass.getDeclaredMethod("fillProfileProperties", gameProfile.getClass(), boolean.class); 293 | method.setAccessible(true); 294 | result = method.invoke(sessionService, gameProfile, true); 295 | } 296 | 297 | return result; 298 | } catch (Exception ex) { 299 | ex.printStackTrace(); 300 | return null; 301 | } 302 | } 303 | 304 | private static boolean existsMethod(Class clazz, String methodName, Class returnClass) { 305 | for (Method method : clazz.getDeclaredMethods()) { 306 | if (method.getName().equals(methodName) && method.getGenericReturnType() == returnClass) { 307 | return true; 308 | } 309 | } 310 | return false; 311 | } 312 | 313 | /** Return: GameProfile */ 314 | public static Object searchUUID(String playerName) { 315 | Class serverClass = getClassByName(getNMSPackageName() + ".MinecraftServer"); 316 | Class userCacheClass = getClassByName(getNMSPackageName() + ".UserCache"); 317 | 318 | try { 319 | Object minecraftServer; 320 | { 321 | Method method = serverClass.getDeclaredMethod("getServer"); 322 | method.setAccessible(true); 323 | minecraftServer = method.invoke(null); 324 | } 325 | 326 | Object userCache; 327 | { 328 | Method method = serverClass.getDeclaredMethod("getUserCache"); 329 | method.setAccessible(true); 330 | userCache = method.invoke(minecraftServer); 331 | } 332 | 333 | { 334 | Method method = userCacheClass.getDeclaredMethod("getProfile", String.class); 335 | method.setAccessible(true); 336 | return method.invoke(userCache, playerName); 337 | } 338 | } catch (Exception ex) { 339 | ex.printStackTrace(); 340 | return null; 341 | } 342 | } 343 | 344 | public static Object createNMSTextComponent(String text) { 345 | if (text == null || text.isEmpty()) { 346 | return null; 347 | } 348 | 349 | Class c = getClassByName(getNMSPackageName() + ".ChatComponentText"); 350 | try { 351 | Constructor constructor = c.getDeclaredConstructor(String.class); 352 | return constructor.newInstance(text); 353 | } catch (Exception ex) { 354 | ex.printStackTrace(); 355 | return null; 356 | } 357 | } 358 | 359 | public static Object toEntityHuman(Player player) { 360 | try { 361 | Class c = getClassByName(getOBCPackageName() + ".entity.CraftPlayer"); 362 | Method m = c.getDeclaredMethod("getHandle"); 363 | m.setAccessible(true); 364 | return m.invoke(player); 365 | } catch (Exception e) { 366 | e.printStackTrace(); 367 | return null; 368 | } 369 | } 370 | 371 | public static Class getClassByName(String name) { 372 | try { 373 | return Class.forName(name); 374 | } catch (Exception e) { 375 | // Class not found 376 | return null; 377 | } 378 | } 379 | 380 | public static Object getField(Class c, Object obj, String key) throws Exception { 381 | Field field = c.getDeclaredField(key); 382 | field.setAccessible(true); 383 | return field.get(obj); 384 | } 385 | 386 | public static void replaceField(Class c, Object obj, String key, Object value) throws Exception { 387 | Field field = c.getDeclaredField(key); 388 | field.setAccessible(true); 389 | field.set(obj, value); 390 | } 391 | 392 | /** 393 | * Get the Package from net.minecraft 394 | * Example: net.minecraft.v1_6_R3 395 | */ 396 | public static String getNMSPackageName() { 397 | return "net.minecraft.server." + Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3]; 398 | } 399 | 400 | /** 401 | * Get the Package from org.bukkit.craftbukkit 402 | * Example: org.bukkit.craftbukkit.v1_6_R3 403 | */ 404 | public static String getOBCPackageName() { 405 | return "org.bukkit.craftbukkit." + Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3]; 406 | } 407 | 408 | } 409 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/util/Cache.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.util; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import org.bukkit.entity.Player; 6 | 7 | public class Cache { 8 | public static Map selectedMobs = new HashMap(); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/util/Config.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.util; 2 | 3 | import java.io.File; 4 | import org.bukkit.configuration.file.YamlConfiguration; 5 | 6 | public class Config { 7 | public static File configFile = new File("plugins/FakeMobs/config.yml"); 8 | public static int SEE_RADIUS = 60; 9 | 10 | public static void load() { 11 | YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); 12 | SEE_RADIUS = config.getInt("Mobs.Radius"); 13 | } 14 | 15 | public static void save() { 16 | YamlConfiguration config = new YamlConfiguration(); 17 | config.set("Mobs.Radius", SEE_RADIUS); 18 | 19 | try { 20 | config.save(configFile); 21 | } catch (Exception e) { 22 | e.printStackTrace(); 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/util/DataWatchCreator.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.util; 2 | 3 | import com.comphenix.protocol.wrappers.WrappedDataWatcher; 4 | import org.bukkit.entity.EntityType; 5 | 6 | public class DataWatchCreator { 7 | 8 | public static WrappedDataWatcher createDefaultWatcher(FakeMob mob) { 9 | WrappedDataWatcher data = new WrappedDataWatcher(); 10 | addEntityDefaults(data, mob.getType()); 11 | 12 | //Custom Name: 13 | if (mob.getCustomName() != null && !mob.getCustomName().isEmpty()) { 14 | data.setObject(11, (byte) 1); 15 | data.setObject(3, (byte) 1); 16 | data.setObject(10, mob.getCustomName()); 17 | data.setObject(2, mob.getCustomName()); 18 | } 19 | 20 | //Sitting: 21 | if (mob.isSitting()) { 22 | if (mob.getType() == EntityType.PLAYER) { 23 | data.setObject(0, (byte) 2); 24 | } else { 25 | data.setObject(16, (byte) 0x1); 26 | } 27 | } 28 | 29 | return data; 30 | } 31 | 32 | public static void addEntityDefaults(WrappedDataWatcher watcher, EntityType type) { 33 | // Add EntityLiving defaults: 34 | watcher.setObject(0, (byte) 0); //Entity options (like invisibility) 35 | watcher.setObject(7, 0); //Potion effect color 36 | watcher.setObject(8, (byte) 0); //Is potion effect active? 37 | watcher.setObject(9, (byte) 0); //Number of Arrows 38 | watcher.setObject(6, (float) 1.0f); //Health 39 | 40 | // Add EntityInsentient defaults: 41 | if (type != EntityType.PLAYER) { 42 | watcher.setObject(11, (byte) 1); //Custom Name Visible (Minecraft 1.7) 43 | watcher.setObject(10, ""); //Custom Name (Minecraft 1.7) 44 | watcher.setObject(3, (byte) 1); //Custom Name Visible (Minecraft 1.8) 45 | watcher.setObject(2, ""); //Custom Name (Minecraft 1.8) 46 | } 47 | 48 | switch (type) { 49 | case BAT: 50 | watcher.setObject(16, (byte) 0); //Is Hanging? 51 | break; 52 | case BLAZE: 53 | watcher.setObject(16, (byte) 0); //On fire 54 | break; 55 | case SPIDER: 56 | case CAVE_SPIDER: 57 | watcher.setObject(16, (byte) 0); //In climbing? 58 | break; 59 | case CHICKEN: 60 | break; 61 | case CREEPER: 62 | watcher.setObject(16, (byte) -1); //1 = Fuse, -1 = idle 63 | watcher.setObject(17, (byte) 0); //Is Powered 64 | break; 65 | case MUSHROOM_COW: 66 | case COW: 67 | break; 68 | case ENDERMAN: 69 | watcher.setObject(16, (short) 0); //Carried Block 70 | watcher.setObject(17, (byte) 0); //Carried Block Data 71 | watcher.setObject(18, (byte) 0); //Is screaming? 72 | break; 73 | case ENDER_DRAGON: 74 | break; 75 | case GHAST: 76 | watcher.setObject(16, (byte) 0); //Is attacking? 77 | break; 78 | case GIANT: 79 | break; 80 | case HORSE: 81 | watcher.setObject(16, 0); 82 | watcher.setObject(19, (byte) 0); //Type: Horse 83 | watcher.setObject(20, 0); //Color: White 84 | watcher.setObject(21, ""); //Owner Name 85 | watcher.setObject(22, 0); //No Armor 86 | break; 87 | case IRON_GOLEM: 88 | watcher.setObject(16, (byte) 0); //Is the iron golem from a player created? 89 | break; 90 | case SLIME: 91 | case MAGMA_CUBE: 92 | watcher.setObject(16, (byte) 1); //Slime size 1 93 | break; 94 | case OCELOT: 95 | watcher.setObject(18, (byte) 0); //Ocelot Type 96 | break; 97 | case PIG: 98 | watcher.setObject(16, (byte) 0); //Has saddle ? 99 | break; 100 | case PIG_ZOMBIE: 101 | case ZOMBIE: 102 | watcher.setObject(12, (byte) 0); //Is child? 103 | watcher.setObject(13, (byte) 0); //Is villager? 104 | watcher.setObject(14, (byte) 0); //Is converting? 105 | break; 106 | case PLAYER: 107 | watcher.setObject(0, (byte) 0); //Player state (Normal, not crouched) 108 | watcher.setObject(10, (byte) 0x7F); //Skin flags 109 | watcher.setObject(16, (byte) 0); //0x02 = Hide cape 110 | watcher.setObject(17, 0.0f); //Absorption hearts 111 | watcher.setObject(18, 0); //Score 112 | break; 113 | case SHEEP: 114 | watcher.setObject(16, (byte) 0); //Color 115 | break; 116 | case SILVERFISH: 117 | break; 118 | case SKELETON: 119 | watcher.setObject(13, (byte) 0); //Type. 0 = Normal, 1 = Wither 120 | break; 121 | case SNOWMAN: 122 | break; 123 | case VILLAGER: 124 | watcher.setObject(16, 0); //Type 125 | break; 126 | case WITCH: 127 | watcher.setObject(21, (byte) 0); //Is agressive? 128 | break; 129 | case WITHER: 130 | watcher.setObject(17, 0); 131 | watcher.setObject(18, 0); 132 | watcher.setObject(19, 0); 133 | watcher.setObject(20, 0); 134 | break; 135 | case WOLF: 136 | watcher.setObject(18, 20.0f); //Health 137 | watcher.setObject(19, (byte) 0); //Begging 138 | watcher.setObject(20, (byte) 14); //Collar color 139 | break; 140 | } 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/util/FakeMob.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.util; 2 | 3 | import com.comphenix.protocol.PacketType; 4 | import de.howaner.FakeMobs.interact.InteractAction; 5 | import com.comphenix.protocol.events.PacketContainer; 6 | import com.comphenix.protocol.wrappers.WrappedDataWatcher; 7 | import com.comphenix.protocol.wrappers.WrappedGameProfile; 8 | import com.comphenix.protocol.wrappers.WrappedSignedProperty; 9 | import com.google.common.base.Charsets; 10 | import com.google.common.collect.Multimap; 11 | import de.howaner.FakeMobs.FakeMobsPlugin; 12 | import de.howaner.FakeMobs.merchant.ReflectionUtils; 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.List; 16 | import java.util.UUID; 17 | import java.util.logging.Level; 18 | import org.bukkit.Bukkit; 19 | import org.bukkit.GameMode; 20 | import org.bukkit.Location; 21 | import org.bukkit.World; 22 | import org.bukkit.entity.EntityType; 23 | import org.bukkit.entity.Player; 24 | 25 | public class FakeMob { 26 | private final int id; 27 | private final UUID uniqueId; 28 | private String name = null; 29 | private Location loc; 30 | private EntityType type; 31 | private WrappedDataWatcher dataWatcher = null; 32 | private final List loadedPlayers = new ArrayList(); 33 | 34 | private boolean sitting = false; 35 | private boolean invisibility = false; 36 | private boolean playerLook = false; 37 | private MobInventory inventory = new MobInventory(); 38 | private MobShop shop = null; 39 | private Multimap playerSkin; // Only used if this.getType() == EntityType.PLAYER 40 | private final List interacts = new ArrayList(); 41 | 42 | public FakeMob(int id, Location loc, EntityType type) { 43 | this.id = id; 44 | this.loc = loc; 45 | this.type = type; 46 | this.uniqueId = UUID.nameUUIDFromBytes(("FakeMob-" + id).getBytes(Charsets.UTF_8)); 47 | this.dataWatcher = DataWatchCreator.createDefaultWatcher(this); 48 | } 49 | 50 | public UUID getUniqueID() { 51 | return this.uniqueId; 52 | } 53 | 54 | public List getInteractActions() { 55 | return this.interacts; 56 | } 57 | 58 | public void clearInteractAction() { 59 | this.interacts.clear(); 60 | } 61 | 62 | public void addInteractAction(InteractAction action) { 63 | this.interacts.add(action); 64 | } 65 | 66 | public void removeInteractAction(InteractAction action) { 67 | this.interacts.remove(action); 68 | } 69 | 70 | public MobInventory getInventory() { 71 | return this.inventory; 72 | } 73 | 74 | public void setInventory(MobInventory inv) { 75 | this.inventory = inv; 76 | if (this.inventory == null) 77 | this.inventory = new MobInventory(); 78 | } 79 | 80 | public boolean haveShop() { 81 | return (this.shop != null); 82 | } 83 | 84 | public MobShop getShop() { 85 | return this.shop; 86 | } 87 | 88 | public void setShop(MobShop shop) { 89 | this.shop = shop; 90 | } 91 | 92 | public int getEntityId() { 93 | return 2300 + this.id; 94 | } 95 | 96 | public Multimap getPlayerSkin() { 97 | return this.playerSkin; 98 | } 99 | 100 | public void setPlayerSkin(Multimap skin) { 101 | this.playerSkin = skin; 102 | } 103 | 104 | public void setPlayerSkin(Player player) { 105 | this.playerSkin = WrappedGameProfile.fromPlayer(player).getProperties(); 106 | } 107 | 108 | public List getNearbyPlayers() { 109 | return this.getNearbyPlayers(3D); 110 | } 111 | 112 | public List getNearbyPlayers(double radius) { 113 | List players = new ArrayList(); 114 | 115 | for (Player player : this.getWorld().getPlayers()) { 116 | if (this.loc.distance(player.getLocation()) <= radius) { 117 | players.add(player); 118 | } 119 | } 120 | 121 | return players; 122 | } 123 | 124 | public void updateInventory() { 125 | for (Player p : this.loadedPlayers) 126 | this.sendInventoryPacket(p); 127 | } 128 | 129 | public boolean isPlayerLoaded(Player player) { 130 | return this.loadedPlayers.contains(player); 131 | } 132 | 133 | public void loadPlayer(Player player) { 134 | if (this.isPlayerLoaded(player)) return; 135 | 136 | this.loadedPlayers.add(player); 137 | this.sendSpawnPacket(player); 138 | } 139 | 140 | public void unloadPlayer(Player player) { 141 | if (!this.isPlayerLoaded(player)) return; 142 | 143 | this.loadedPlayers.remove(player); 144 | this.sendDestroyPacket(player); 145 | } 146 | 147 | public boolean isInRange(Player player) { 148 | return this.loc.getWorld() == player.getLocation().getWorld() && (this.loc.distance(player.getLocation()) <= 48D); 149 | } 150 | 151 | public int getId() { 152 | return this.id; 153 | } 154 | 155 | public String getCustomName() { 156 | return this.name; 157 | } 158 | 159 | public Location getLocation() { 160 | return this.loc; 161 | } 162 | 163 | public World getWorld() { 164 | return this.loc.getWorld(); 165 | } 166 | 167 | public EntityType getType() { 168 | return this.type; 169 | } 170 | 171 | public boolean isSitting() { 172 | return this.sitting; 173 | } 174 | 175 | public boolean isInvisibility() { 176 | return this.invisibility; 177 | } 178 | 179 | public boolean isPlayerLook() { 180 | return this.playerLook; 181 | } 182 | 183 | public void setLocation(Location loc) { 184 | this.loc = loc; 185 | } 186 | 187 | public void setCustomName(String name) { 188 | if (name != null && name.length() > 32) name = name.substring(0, 32); 189 | this.name = name; 190 | 191 | if (this.name != null && this.name.isEmpty()) { 192 | this.name = null; 193 | } 194 | 195 | if (this.type == EntityType.PLAYER) { 196 | // No need to change the watcher 197 | return; 198 | } 199 | 200 | if (this.name == null) { 201 | this.dataWatcher.setObject(11, (byte) 0); 202 | this.dataWatcher.setObject(10, ""); 203 | this.dataWatcher.setObject(3, (byte) 0); 204 | this.dataWatcher.setObject(2, ""); 205 | } else { 206 | this.dataWatcher.setObject(11, (byte) 1); 207 | this.dataWatcher.setObject(10, this.name); 208 | this.dataWatcher.setObject(3, (byte) 1); 209 | this.dataWatcher.setObject(2, this.name); 210 | } 211 | } 212 | 213 | public void setSitting(boolean sitting) { 214 | if (this.type != EntityType.OCELOT && this.type != EntityType.WOLF && this.type != EntityType.PLAYER) return; 215 | if (this.sitting == sitting) return; 216 | this.sitting = sitting; 217 | 218 | if (this.getType() == EntityType.PLAYER) { 219 | byte current = this.dataWatcher.getByte(0); 220 | if (sitting) 221 | this.dataWatcher.setObject(0, (byte) (current | 1 << 1)); 222 | else 223 | this.dataWatcher.setObject(0, (byte) (current & (1 << 1 ^ 0xFFFFFFFF))); 224 | } else if (sitting) { 225 | this.dataWatcher.setObject(16, (byte) 0x1); 226 | } else { 227 | this.dataWatcher.setObject(16, (byte) 0x0); 228 | } 229 | } 230 | 231 | public void setInvisibility(boolean invisibility) { 232 | if (this.invisibility == invisibility) return; 233 | this.invisibility = invisibility; 234 | 235 | byte current = this.dataWatcher.getByte(0); 236 | if (invisibility) { 237 | this.dataWatcher.setObject(0, (byte) (current | 1 << 5)); 238 | } else { 239 | this.dataWatcher.setObject(0, (byte) (current & (1 << 5 ^ 0xFFFFFFFF))); 240 | } 241 | } 242 | 243 | public void setPlayerLook(boolean look) { 244 | if (this.playerLook == look) return; 245 | 246 | if (!look) { 247 | for (Player player : this.loadedPlayers) 248 | this.sendLookPacket(player, this.getLocation().getYaw()); 249 | } 250 | 251 | this.playerLook = look; 252 | } 253 | 254 | public void teleport(Location loc) { 255 | this.loc = loc; 256 | 257 | for (Player player : this.loadedPlayers) 258 | this.sendPositionPacket(player); 259 | } 260 | 261 | public void setType(EntityType type) { 262 | if (type == null || this.type == type || !type.isAlive()) return; 263 | 264 | for (Player p : this.loadedPlayers) 265 | this.sendDestroyPacket(p); 266 | 267 | this.type = type; 268 | this.dataWatcher = DataWatchCreator.createDefaultWatcher(this); 269 | 270 | for (Player p : this.loadedPlayers) 271 | this.sendSpawnPacket(p); 272 | } 273 | 274 | public void updateMetadata() { 275 | for (Player player : this.loadedPlayers) { 276 | this.sendMetaPacket(player); 277 | } 278 | } 279 | 280 | public void updateCustomName() { 281 | for (Player player : this.loadedPlayers) { 282 | if (this.getType() == EntityType.PLAYER) { 283 | this.sendDestroyPacket(player); 284 | } else 285 | this.sendMetaPacket(player); 286 | } 287 | 288 | // Need a 5 tick delay because mojang did mistakes in 1.8 ... 289 | if (this.getType() == EntityType.PLAYER) { 290 | Bukkit.getScheduler().runTaskLater(FakeMobsPlugin.getPlugin(), new Runnable() { 291 | @Override 292 | public void run() { 293 | for (Player player : FakeMob.this.loadedPlayers) { 294 | FakeMob.this.sendSpawnPacket(player); 295 | } 296 | } 297 | }, 5L); 298 | } 299 | } 300 | 301 | 302 | //////////////// -- PACKETS -- //////////////// 303 | 304 | public void sendSpawnPacket(Player player) { 305 | if (this.getType() == EntityType.PLAYER) 306 | this.sendPlayerSpawnPacket(player); 307 | else 308 | this.sendEntitySpawnPacket(player); 309 | } 310 | 311 | public void sendPlayerSpawnPacket(final Player player) { 312 | PacketContainer packet = FakeMobsPlugin.getPlugin().getProtocolManager().createPacket(PacketType.Play.Server.NAMED_ENTITY_SPAWN); 313 | 314 | packet.getIntegers().write(0, this.getEntityId()); 315 | packet.getIntegers().write(1, (int) Math.floor(this.loc.getX() * 32D)); //X 316 | packet.getIntegers().write(2, (int) Math.floor(this.loc.getY() * 32D)); //Y 317 | packet.getIntegers().write(3, (int) Math.floor(this.loc.getZ() * 32D)); //Z 318 | packet.getIntegers().write(4, 0); //Item in Hand Slot 319 | 320 | packet.getBytes().write(0, (byte)(int)(this.loc.getYaw() * 256.0F / 360.0F)); //Yaw 321 | packet.getBytes().write(1, (byte)(int)(this.loc.getPitch() * 256.0F / 360.0F)); //Pitch 322 | 323 | final WrappedGameProfile profile = new WrappedGameProfile(this.uniqueId, (this.getCustomName() == null) ? "No Name" : this.getCustomName()); 324 | if (this.playerSkin != null) { 325 | profile.getProperties().putAll(this.playerSkin); 326 | } 327 | 328 | final boolean isSpigot18 = (packet.getGameProfiles().size() == 0); 329 | if (isSpigot18) 330 | packet.getSpecificModifier(UUID.class).write(0, profile.getUUID()); 331 | else 332 | packet.getGameProfiles().write(0, profile); 333 | packet.getDataWatcherModifier().write(0, this.dataWatcher); 334 | 335 | int protocolVersion = FakeMobsPlugin.getPlugin().getProtocolManager().getProtocolVersion(player); 336 | if (protocolVersion >= 47 || protocolVersion == Integer.MIN_VALUE) { 337 | PacketContainer infoPacket = FakeMobsPlugin.getPlugin().getProtocolManager().createPacket(PacketType.Play.Server.PLAYER_INFO); 338 | 339 | if (isSpigot18) { 340 | Object playerInfo = ReflectionUtils.createPlayerInfoData(profile.getHandle(), GameMode.SURVIVAL, 0, " "); 341 | infoPacket.getSpecificModifier(ReflectionUtils.PlayerInfoAction.getNMSClass()).write(0, ReflectionUtils.PlayerInfoAction.ADD_PLAYER); 342 | infoPacket.getSpecificModifier(List.class).write(0, Arrays.asList(new Object[] { playerInfo })); 343 | } else { 344 | infoPacket.getIntegers().write(0, 0); //Packet: Create 345 | infoPacket.getIntegers().write(1, 0); //Gamemode: Survival 346 | infoPacket.getIntegers().write(2, 0); //Ping: 0 347 | 348 | infoPacket.getGameProfiles().write(0, profile); 349 | } 350 | 351 | try { 352 | FakeMobsPlugin.getPlugin().getProtocolManager().sendServerPacket(player, infoPacket); 353 | } catch (Exception e) { 354 | FakeMobsPlugin.log.log(Level.SEVERE, "Can''t send player info packet to {0}", player.getName()); 355 | e.printStackTrace(); 356 | } 357 | 358 | Bukkit.getScheduler().runTaskLater(FakeMobsPlugin.getPlugin(), new Runnable() { 359 | @Override 360 | public void run() { 361 | if (!FakeMob.this.isPlayerLoaded(player)) { 362 | return; 363 | } 364 | PacketContainer infoPacket = FakeMobsPlugin.getPlugin().getProtocolManager().createPacket(PacketType.Play.Server.PLAYER_INFO); 365 | 366 | if (isSpigot18) { 367 | Object playerInfo = ReflectionUtils.createPlayerInfoData(profile.getHandle(), GameMode.SURVIVAL, 0, ""); 368 | infoPacket.getSpecificModifier(ReflectionUtils.PlayerInfoAction.getNMSClass()).write(0, ReflectionUtils.PlayerInfoAction.REMOVE_PLAYER); 369 | infoPacket.getSpecificModifier(List.class).write(0, Arrays.asList(new Object[] { playerInfo })); 370 | } else { 371 | infoPacket.getIntegers().write(0, 4); //Packet: Remove 372 | infoPacket.getIntegers().write(1, 0); //Gamemode: Survival 373 | infoPacket.getIntegers().write(2, 0); //Ping: 0 374 | 375 | infoPacket.getGameProfiles().write(0, profile); 376 | } 377 | 378 | try { 379 | FakeMobsPlugin.getPlugin().getProtocolManager().sendServerPacket(player, infoPacket); 380 | } catch (Exception e) { 381 | FakeMobsPlugin.log.log(Level.WARNING, "Can''t send player info packet to {0}", player.getName()); 382 | e.printStackTrace(); 383 | } 384 | } 385 | }, (this.playerSkin == null ? 5L : 40L)); 386 | } 387 | 388 | try { 389 | FakeMobsPlugin.getPlugin().getProtocolManager().sendServerPacket(player, packet); 390 | } catch (Exception e) { 391 | FakeMobsPlugin.log.log(Level.SEVERE, "Can''t send spawn packet to {0} from mob #{1}", new Object[]{ player.getName(), this.getId() }); 392 | e.printStackTrace(); 393 | return; 394 | } 395 | 396 | this.sendLookPacket(player, this.loc.getYaw()); 397 | this.sendInventoryPacket(player); 398 | } 399 | 400 | public void sendEntitySpawnPacket(Player player) { 401 | PacketContainer packet = FakeMobsPlugin.getPlugin().getProtocolManager().createPacket(PacketType.Play.Server.SPAWN_ENTITY_LIVING); 402 | 403 | packet.getIntegers().write(0, this.getEntityId()); 404 | packet.getIntegers().write(1, (int) this.type.getTypeId()); //Id 405 | packet.getIntegers().write(2, (int) Math.floor(this.loc.getX() * 32D)); //X 406 | packet.getIntegers().write(3, (int) Math.floor((this.loc.getY() + 0.001D) * 32D)); //Y 407 | packet.getIntegers().write(4, (int) Math.floor(this.loc.getZ() * 32D)); //Z 408 | 409 | packet.getBytes().write(0, (byte)(int)(this.loc.getYaw() * 256.0F / 360.0F)); //Yaw 410 | packet.getBytes().write(1, (byte)(int)(this.loc.getPitch() * 256.0F / 360.0F)); //Pitch 411 | packet.getBytes().write(2, (byte)(int)(this.loc.getYaw() * 256.0F / 360.0F)); //Head 412 | 413 | packet.getDataWatcherModifier().write(0, this.dataWatcher); 414 | 415 | try { 416 | FakeMobsPlugin.getPlugin().getProtocolManager().sendServerPacket(player, packet); 417 | } catch (Exception e) { 418 | FakeMobsPlugin.log.log(Level.SEVERE, "Can''t send spawn packet to {0} from mob #{1}", new Object[]{player.getName(), this.getId()}); 419 | e.printStackTrace(); 420 | return; 421 | } 422 | 423 | this.sendInventoryPacket(player); 424 | } 425 | 426 | public void sendMetaPacket(Player player) { 427 | PacketContainer packet = FakeMobsPlugin.getPlugin().getProtocolManager().createPacket(PacketType.Play.Server.ENTITY_METADATA); 428 | 429 | packet.getIntegers().write(0, this.getEntityId()); 430 | packet.getWatchableCollectionModifier().write(0, this.dataWatcher.getWatchableObjects()); 431 | 432 | try { 433 | FakeMobsPlugin.getPlugin().getProtocolManager().sendServerPacket(player, packet); 434 | } catch (Exception e) { 435 | FakeMobsPlugin.log.log(Level.SEVERE, "Can''t send metadata oacket to {0} from mob #{1}", new Object[]{player.getName(), this.getId()}); 436 | e.printStackTrace(); 437 | } 438 | } 439 | 440 | public void sendInventoryPacket(Player player) { 441 | List packets = this.inventory.createPackets(this.getEntityId()); 442 | if (packets.isEmpty()) return; 443 | 444 | try { 445 | for (PacketContainer packet : packets) 446 | FakeMobsPlugin.getPlugin().getProtocolManager().sendServerPacket(player, packet); 447 | } catch (Exception e) { 448 | FakeMobsPlugin.log.log(Level.SEVERE, "Can''t send inventory packets to {0} from mob #{1}", new Object[]{player.getName(), this.getId()}); 449 | e.printStackTrace(); 450 | } 451 | } 452 | 453 | public void sendLookPacket(Player player, Location point) { 454 | double xDiff = point.getX() - this.loc.getX(); 455 | //double yDiff = point.getY() - this.loc.getY(); 456 | double zDiff = point.getZ() - this.loc.getZ(); 457 | double DistanceXZ = Math.sqrt(xDiff * xDiff + zDiff * zDiff); 458 | //double DistanceY = Math.sqrt(DistanceXZ * DistanceXZ + yDiff * yDiff); 459 | double newYaw = Math.acos(xDiff / DistanceXZ) * 180.0D / 3.141592653589793D; 460 | //double newPitch = Math.acos(yDiff / DistanceY) * 180.0D / 3.141592653589793D - 90.0D; 461 | if (zDiff < 0.0D) 462 | newYaw += Math.abs(180.0D - newYaw) * 2.0D; 463 | double yaw = ((float)newYaw - 98.0D); 464 | 465 | this.sendLookPacket(player, yaw); 466 | } 467 | 468 | public void sendLookPacket(Player player, double yaw) { 469 | PacketContainer packet = FakeMobsPlugin.getPlugin().getProtocolManager().createPacket(PacketType.Play.Server.ENTITY_HEAD_ROTATION); 470 | 471 | packet.getIntegers().write(0, this.getEntityId()); 472 | packet.getBytes().write(0, (byte)(int)(yaw * 256.0F / 360.0F)); 473 | 474 | try { 475 | FakeMobsPlugin.getPlugin().getProtocolManager().sendServerPacket(player, packet); 476 | } catch (Exception e) { 477 | FakeMobsPlugin.log.log(Level.SEVERE, "Can''t send look packet to {0} from mob #{1}", new Object[]{player.getName(), this.getId()}); 478 | e.printStackTrace(); 479 | } 480 | } 481 | 482 | public void sendPositionPacket(Player player) { 483 | PacketContainer packet = FakeMobsPlugin.getPlugin().getProtocolManager().createPacket(PacketType.Play.Server.ENTITY_TELEPORT); 484 | 485 | packet.getIntegers().write(0, this.getEntityId()); //Id 486 | packet.getIntegers().write(1, (int) Math.floor(this.loc.getX() * 32)); //X 487 | packet.getIntegers().write(2, (int) Math.floor((this.loc.getY() + 0.001D) * 32)); //Y 488 | packet.getIntegers().write(3, (int) Math.floor(this.loc.getZ() * 32)); //Z 489 | 490 | packet.getBytes().write(0, (byte)(int)(this.loc.getYaw() * 256.0F / 360.0F)); //Yaw 491 | packet.getBytes().write(1, (byte)(int)(this.loc.getPitch() * 256.0F / 360.0F)); //Pitch 492 | 493 | try { 494 | FakeMobsPlugin.getPlugin().getProtocolManager().sendServerPacket(player, packet); 495 | } catch (Exception e) { 496 | FakeMobsPlugin.log.log(Level.SEVERE, "Can''t send position packet to {0} from mob #{1}", new Object[]{player.getName(), this.getId()}); 497 | e.printStackTrace(); 498 | } 499 | } 500 | 501 | public void sendDestroyPacket(Player player) { 502 | PacketContainer packet = FakeMobsPlugin.getPlugin().getProtocolManager().createPacket(PacketType.Play.Server.ENTITY_DESTROY); 503 | packet.getIntegerArrays().write(0, new int[] { this.getEntityId() }); 504 | 505 | try { 506 | FakeMobsPlugin.getPlugin().getProtocolManager().sendServerPacket(player, packet); 507 | } catch (Exception e) { 508 | FakeMobsPlugin.log.log(Level.SEVERE, "Can''t send destroy packet to {0} from mob #{1}", new Object[]{player.getName(), this.getId()}); 509 | e.printStackTrace(); 510 | return; 511 | } 512 | 513 | if (FakeMobsPlugin.getPlugin().getProtocolManager().getProtocolVersion(player) >= 47 && this.getType() == EntityType.PLAYER) { 514 | WrappedGameProfile profile = new WrappedGameProfile(this.uniqueId, (this.getCustomName() == null) ? "No Name" : this.getCustomName()); 515 | PacketContainer infoPacket = FakeMobsPlugin.getPlugin().getProtocolManager().createPacket(PacketType.Play.Server.PLAYER_INFO); 516 | 517 | boolean spigot18 = (infoPacket.getIntegers().size() == 0); 518 | if (spigot18) { 519 | Object playerInfo = ReflectionUtils.createPlayerInfoData(profile.getHandle(), GameMode.SURVIVAL, 0, ""); 520 | infoPacket.getSpecificModifier(ReflectionUtils.PlayerInfoAction.getNMSClass()).write(0, ReflectionUtils.PlayerInfoAction.REMOVE_PLAYER); 521 | infoPacket.getSpecificModifier(List.class).write(0, Arrays.asList(new Object[] { playerInfo })); 522 | } else { 523 | infoPacket.getIntegers().write(0, 4); 524 | infoPacket.getIntegers().write(1, 0); 525 | infoPacket.getIntegers().write(2, 0); 526 | 527 | infoPacket.getGameProfiles().write(0, profile); 528 | } 529 | 530 | try { 531 | FakeMobsPlugin.getPlugin().getProtocolManager().sendServerPacket(player, infoPacket); 532 | } catch (Exception e) { 533 | FakeMobsPlugin.log.log(Level.SEVERE, "Can''t send player info destroy packet to {0}", player.getName()); 534 | e.printStackTrace(); 535 | } 536 | } 537 | } 538 | 539 | } 540 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/util/ItemParser.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.util; 2 | 3 | import java.lang.reflect.Method; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.Material; 6 | import org.bukkit.inventory.ItemStack; 7 | 8 | public class ItemParser { 9 | 10 | public static ItemStack generateItemStack(String[] input) throws Exception { 11 | if (input[0].equalsIgnoreCase("none")) return new ItemStack(Material.AIR); 12 | 13 | // Material 14 | Material material = Material.matchMaterial(input[0]); 15 | if (material == null) 16 | throw new Exception("Unknown item type"); 17 | ItemStack item = new ItemStack(material); 18 | 19 | // Amount 20 | if (input.length >= 2) { 21 | Integer amount = toInt(input[1], 1, 64); 22 | if (amount == null) 23 | throw new Exception("Invalid item amount"); 24 | 25 | amount = Math.max(Math.min(64, amount), 1); 26 | item.setAmount(amount); 27 | } 28 | 29 | // Meta 30 | if (input.length >= 3) { 31 | Integer meta = toInt(input[2], 0, 2147483647); 32 | if (meta == null) 33 | throw new Exception("Invalid meta"); 34 | 35 | item.setDurability((short)(int)meta); 36 | } 37 | 38 | // NBT Data 39 | if (input.length >= 4) { 40 | StringBuilder builder = new StringBuilder(); 41 | for (int i = 3; i < input.length; i++) { 42 | if (i != 3) builder.append(" "); 43 | builder.append(input[i]); 44 | } 45 | 46 | try { 47 | Class MojangsonParserClass = Class.forName("net.minecraft.server." + getBukkitVersion() + ".MojangsonParser"); 48 | Method parseMethod = MojangsonParserClass.getDeclaredMethod("parse", String.class); 49 | parseMethod.setAccessible(true); 50 | Object NBTBase = parseMethod.invoke(null, builder.toString()); 51 | 52 | if (NBTBase.getClass() != Class.forName("net.minecraft.server." + getBukkitVersion() + ".NBTTagCompound")) { 53 | throw new Exception("Invalid nbt tag: Not a valid tag"); 54 | } 55 | item = addNBTToItemStack(item, NBTBase); 56 | } catch (Exception e) { 57 | throw new Exception("Invalid nbt tag: " + e.getMessage()); 58 | } 59 | } 60 | 61 | return item; 62 | } 63 | 64 | public static Integer toInt(String obj, int min, int max) { 65 | try { 66 | int number = Integer.parseInt(obj); 67 | 68 | number = Math.max(Math.min(max, number), min); 69 | return number; 70 | } catch (Exception e) { 71 | return null; 72 | } 73 | } 74 | 75 | public static ItemStack addNBTToItemStack(ItemStack stack, Object nbt) { 76 | try { 77 | Class CraftItemStackClass = Class.forName("org.bukkit.craftbukkit." + getBukkitVersion() + ".inventory.CraftItemStack"); 78 | Class NMSItemStackClass = Class.forName("net.minecraft.server." + getBukkitVersion() + ".ItemStack"); 79 | Class NBTTagCompoundClass = Class.forName("net.minecraft.server." + getBukkitVersion() + ".NBTTagCompound"); 80 | 81 | Method asNMSCopy = CraftItemStackClass.getDeclaredMethod("asNMSCopy", ItemStack.class); 82 | asNMSCopy.setAccessible(true); 83 | Object nmsStack = asNMSCopy.invoke(null, stack); 84 | 85 | Method setTag = NMSItemStackClass.getDeclaredMethod("setTag", NBTTagCompoundClass); 86 | setTag.setAccessible(true); 87 | setTag.invoke(nmsStack, nbt); 88 | 89 | Method asBukkitCopy = CraftItemStackClass.getDeclaredMethod("asBukkitCopy", NMSItemStackClass); 90 | asBukkitCopy.setAccessible(true); 91 | return (ItemStack) asBukkitCopy.invoke(null, nmsStack); 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | return null; 95 | } 96 | } 97 | 98 | public static String getBukkitVersion() { 99 | return Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3]; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/util/LookUpdate.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.util; 2 | 3 | import de.howaner.FakeMobs.FakeMobsPlugin; 4 | import java.util.List; 5 | import org.bukkit.entity.Player; 6 | 7 | public class LookUpdate implements Runnable { 8 | private FakeMobsPlugin plugin; 9 | 10 | public LookUpdate(FakeMobsPlugin plugin) { 11 | this.plugin = plugin; 12 | } 13 | 14 | @Override 15 | public void run() { 16 | try { 17 | for (FakeMob mob : this.plugin.getMobs()) { 18 | if (!mob.isPlayerLook()) continue; 19 | List players = mob.getNearbyPlayers(5D); 20 | for (Player p : players) 21 | mob.sendLookPacket(p, p.getLocation()); 22 | } 23 | } catch (Exception e) { 24 | //Do Nothing 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/util/MobInventory.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.util; 2 | 3 | import com.comphenix.protocol.Packets; 4 | import com.comphenix.protocol.events.PacketContainer; 5 | import de.howaner.FakeMobs.FakeMobsPlugin; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import org.bukkit.Material; 9 | import org.bukkit.inventory.ItemStack; 10 | 11 | public class MobInventory { 12 | // 0 - Item in Hand, 1 = Boots, 2 = Leggings, 3 = ChestPlate, 4 = Helmet 13 | private ItemStack[] items = new ItemStack[5]; 14 | 15 | public ItemStack getItemInHand() { 16 | return this.items[0]; 17 | } 18 | 19 | public ItemStack getBoots() { 20 | return this.items[1]; 21 | } 22 | 23 | public ItemStack getLeggings() { 24 | return this.items[2]; 25 | } 26 | 27 | public ItemStack getChestPlate() { 28 | return this.items[3]; 29 | } 30 | 31 | public ItemStack getHelmet() { 32 | return this.items[4]; 33 | } 34 | 35 | public void setItemInHand(ItemStack item) { 36 | this.setSlot(0, item); 37 | } 38 | 39 | public void setBoots(ItemStack item) { 40 | this.setSlot(1, item); 41 | } 42 | 43 | public void setLeggings(ItemStack item) { 44 | this.setSlot(2, item); 45 | } 46 | 47 | public void setChestPlate(ItemStack item) { 48 | this.setSlot(3, item); 49 | } 50 | 51 | public void setHelmet(ItemStack item) { 52 | this.setSlot(4, item); 53 | } 54 | 55 | public ItemStack getSlot(int slot) { 56 | if (slot < 0 || slot >= this.items.length) { 57 | return null; 58 | } 59 | 60 | return this.items[slot]; 61 | } 62 | 63 | public void setSlot(int slot, ItemStack item) { 64 | if (item != null && item.getType() == Material.AIR) { 65 | item = null; 66 | } 67 | 68 | if (slot < 0 || slot >= this.items.length) { 69 | return; 70 | } 71 | 72 | this.items[slot] = item; 73 | } 74 | 75 | public List createPackets(int entityId) { 76 | List packetList = new ArrayList(); 77 | for (int i = 0; i < 5; i++) { 78 | ItemStack stack = this.getSlot(i); 79 | 80 | PacketContainer packet = FakeMobsPlugin.getPlugin().getProtocolManager().createPacket(Packets.Server.ENTITY_EQUIPMENT); 81 | packet.getIntegers().write(0, entityId); 82 | packet.getIntegers().write(1, i); 83 | packet.getItemModifier().write(0, stack); 84 | 85 | packetList.add(packet); 86 | } 87 | return packetList; 88 | } 89 | 90 | public boolean isEmpty() { 91 | for (ItemStack item : this.items) { 92 | if (item != null && item.getType() != Material.AIR) { 93 | return false; 94 | } 95 | } 96 | return true; 97 | } 98 | 99 | @Override 100 | public MobInventory clone() { 101 | MobInventory inv = new MobInventory(); 102 | for (int i = 0; i < 5; i++) { 103 | inv.setSlot(i, this.getSlot(i)); 104 | } 105 | return inv; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/util/MobShop.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.util; 2 | 3 | import de.howaner.FakeMobs.merchant.Merchant; 4 | import de.howaner.FakeMobs.merchant.MerchantOffer; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import org.bukkit.ChatColor; 8 | import org.bukkit.Material; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.inventory.ItemStack; 11 | import org.bukkit.inventory.meta.ItemMeta; 12 | 13 | public class MobShop { 14 | private List items = new ArrayList(); 15 | 16 | public void openShop(Player player) { 17 | this.openShop(player, null); 18 | } 19 | 20 | public void openShop(Player player, String title) { 21 | Merchant merchant = new Merchant(); 22 | for (MerchantOffer offer : items) 23 | merchant.addOffer(offer); 24 | merchant.setTitle(title); 25 | merchant.openTrading(player); 26 | } 27 | 28 | public void clear() { 29 | this.items.clear(); 30 | } 31 | 32 | public void addItems(MerchantOffer... items) { 33 | for (MerchantOffer item : items) 34 | this.items.add(item); 35 | } 36 | 37 | public void addItem(MerchantOffer item) { 38 | this.items.add(item); 39 | } 40 | 41 | public MerchantOffer getItem(int id) { 42 | return this.items.get(id); 43 | } 44 | 45 | public List getItems() { 46 | return this.items; 47 | } 48 | 49 | public void removeItem(MerchantOffer offer) { 50 | this.items.remove(offer); 51 | } 52 | 53 | public void removeItem(int id) { 54 | this.items.remove(id); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/de/howaner/FakeMobs/util/SkinQueue.java: -------------------------------------------------------------------------------- 1 | package de.howaner.FakeMobs.util; 2 | 3 | import com.comphenix.protocol.wrappers.WrappedGameProfile; 4 | import de.howaner.FakeMobs.FakeMobsPlugin; 5 | import de.howaner.FakeMobs.merchant.ReflectionUtils; 6 | import java.util.Queue; 7 | import java.util.UUID; 8 | import java.util.concurrent.LinkedBlockingQueue; 9 | 10 | public class SkinQueue extends Thread { 11 | private final Queue queue = new LinkedBlockingQueue(); 12 | 13 | public SkinQueue() { 14 | super("FakeMobs skin queue"); 15 | this.setDaemon(true); 16 | } 17 | 18 | @Override 19 | public void run() { 20 | while (!this.isInterrupted()) { 21 | this.blockThread(); 22 | while (!this.queue.isEmpty()) { 23 | SkinEntry entry = this.queue.poll(); 24 | if (FakeMobsPlugin.getPlugin().getMob(entry.getMob().getId()) != entry.getMob()) continue; 25 | 26 | Object nmsProfile = ReflectionUtils.searchUUID(entry.getSkinName()); 27 | WrappedGameProfile profile = nmsProfile == null ? new WrappedGameProfile((UUID) null, entry.getSkinName()) : WrappedGameProfile.fromHandle(nmsProfile); 28 | 29 | if (profile.getProperties().isEmpty() && profile.isComplete()) 30 | profile = WrappedGameProfile.fromHandle(ReflectionUtils.fillProfileProperties(profile.getHandle())); 31 | if (profile == null) continue; 32 | 33 | entry.getMob().setPlayerSkin(profile.getProperties()); 34 | entry.getMob().updateCustomName(); 35 | FakeMobsPlugin.getPlugin().saveMobsFile(); 36 | } 37 | } 38 | } 39 | 40 | public synchronized void addToQueue(FakeMob mob, String skinName) { 41 | this.queue.add(new SkinEntry(mob, skinName)); 42 | this.notify(); 43 | } 44 | 45 | private synchronized void blockThread() { 46 | try { 47 | while (this.queue.isEmpty()) { 48 | this.wait(); 49 | } 50 | } catch (Exception ex) { } 51 | } 52 | 53 | private static class SkinEntry { 54 | private final FakeMob mob; 55 | private final String skinName; 56 | 57 | public SkinEntry(FakeMob mob, String skinName) { 58 | this.mob = mob; 59 | this.skinName = skinName; 60 | } 61 | 62 | public FakeMob getMob() { 63 | return this.mob; 64 | } 65 | 66 | public String getSkinName() { 67 | return this.skinName; 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: FakeMobs 2 | version: ${project.version} 3 | author: Howaner 4 | description: A simple npc plugin 5 | depend: [ProtocolLib] 6 | main: de.howaner.FakeMobs.FakeMobsPlugin 7 | 8 | commands: 9 | FakeMob: 10 | description: FakeMobs Manage 11 | usage: "§4Bad Syntax. Type /FakeMob help for Help!" 12 | permissions: 13 | FakeMobs.create: 14 | description: Spawn Fake Mobs 15 | default: op 16 | FakeMobs.select: 17 | description: Select Fake Mobs 18 | default: op 19 | FakeMobs.name: 20 | description: Give Fake Mob a name 21 | default: op 22 | FakeMobs.sitting: 23 | description: Can sit the Fake Mob 24 | default: op 25 | FakeMobs.invisibility: 26 | description: Can make a Fake Mob Invisibility 27 | default: op 28 | FakeMobs.look: 29 | description: Can change the look Function of a Mob 30 | default: op 31 | FakeMobs.teleport: 32 | description: Teleport a Fake Mob 33 | default: op 34 | FakeMobs.inv: 35 | description: Can edit the Inventory of a Fake Mob 36 | default: op 37 | FakeMobs.shop: 38 | description: Permission to manage the Shop of a Fake mob 39 | default: op 40 | FakeMobs.interact: 41 | description: Permission to manage the Interact Actions of a Fake mob 42 | default: op 43 | FakeMobs.skin: 44 | description: Permission to set the skin from a player 45 | default: op 46 | FakeMobs.remove: 47 | description: Remove Fake Mobs 48 | default: op 49 | FakeMobs.help: 50 | description: Can see the Help page 51 | FakeMobs.*: 52 | description: All Permissions for FakeMobs 53 | children: 54 | FakeMobs.create: true 55 | FakeMobs.select: true 56 | FakeMobs.name: true 57 | FakeMobs.sitting: true 58 | FakeMobs.invisibility: true 59 | FakeMobs.look: true 60 | FakeMobs.teleport: true 61 | FakeMobs.inv: true 62 | FakeMobs.shop: true 63 | FakeMobs.interact: true 64 | FakeMobs.skin: true 65 | FakeMobs.remove: true 66 | FakeMobs.help: true 67 | default: op 68 | --------------------------------------------------------------------------------