Last active
August 29, 2015 14:21
-
-
Save justinoboyle/e5cb934f6c7b7027bf8c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.FileNotFoundException; | |
import java.io.PrintWriter; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Scanner; | |
import java.util.UUID; | |
import net.minecraft.server.v1_8_R1.AttributeInstance; | |
import net.minecraft.server.v1_8_R1.EntityInsentient; | |
import net.minecraft.server.v1_8_R1.GenericAttributes; | |
import net.minecraft.server.v1_8_R1.PathEntity; | |
import org.bukkit.Bukkit; | |
import org.bukkit.Location; | |
import org.bukkit.OfflinePlayer; | |
import org.bukkit.World; | |
import org.bukkit.craftbukkit.v1_8_R1.entity.CraftEntity; | |
import org.bukkit.entity.Entity; | |
import org.bukkit.entity.EntityType; | |
import org.bukkit.entity.LivingEntity; | |
import org.bukkit.entity.Player; | |
import org.bukkit.event.EventHandler; | |
import org.bukkit.event.HandlerList; | |
import org.bukkit.event.Listener; | |
import org.bukkit.event.player.PlayerQuitEvent; | |
import org.bukkit.event.player.PlayerTeleportEvent; | |
import org.bukkit.plugin.Plugin; | |
import org.bukkit.plugin.java.JavaPlugin; | |
import org.bukkit.scheduler.BukkitRunnable; | |
/** | |
* | |
* @author Justin O'Boyle for mike1665 | https://www.justinoboyle.com/ | |
* | |
*/ | |
public class Pet implements Listener { | |
private static List<Pet> pets = new ArrayList<Pet>(); // The list that | |
// stores all active | |
// pets, should never | |
// be saved directly. | |
private static final EntityType DEFAULT_TYPE = EntityType.PIG; // The | |
// default | |
// type of | |
// pet, if | |
// something | |
// goes | |
// wrong. | |
private static final String NO_NAME = "null"; // The String to use if you do | |
// not want the pet to have a | |
// name. | |
private OfflinePlayer owner; // The pet's owner. | |
private String name; // The name of the pet. | |
private EntityType type; // The type of entity to spawn. | |
private LivingEntity temp; // The temporary entity for the pet, should | |
// *NEVER* be saved! | |
private UUID tempUUID; // The UUID of the pet, used for saving. | |
private boolean done = false; // Is the pet alive or should all runnables | |
// clean themselves up? | |
public static JavaPlugin MAIN_CLASS; | |
/** | |
* Run this in your onEnable(); | |
*/ | |
public static void handleEnable(JavaPlugin main) { | |
MAIN_CLASS = main; | |
try { | |
ArrayList<String> ss = addToActiveList(null, true); | |
for (String s : ss) { | |
for (World w : Bukkit.getWorlds()) | |
for (Entity e : w.getEntities()) { | |
if (e.getUniqueId().toString().equals(s)) { | |
e.remove(); | |
} | |
} | |
} | |
} catch (Exception ex) { | |
// The file doesn't exist, that's fine. | |
} | |
} | |
/** | |
* Create a new pet object! | |
* | |
* @param owner | |
* The pet owner. | |
* @param name | |
* The pet's name; you can use Pet.NO_NAME if you'd like no name. | |
* @param type | |
* The pet's type; you can use Pet.DEFAULT_TYPE if you'd like to | |
* use the default. | |
*/ | |
public Pet(OfflinePlayer owner, String name, EntityType type) { | |
super(); | |
Bukkit.getServer().getPluginManager().registerEvents(this, MAIN_CLASS); | |
this.owner = owner; | |
this.name = name; | |
this.type = type; | |
if (!pets.contains(this)) // Make sure there's no duplicates. | |
pets.add(this); | |
} | |
/** | |
* Spawn the pet in the world, and if it already exists, replace the old one | |
* with a new pet. | |
*/ | |
public void spawn() { | |
if (done) { | |
return; | |
} | |
// Make sure there can only be one instance of each pet in the world. | |
if (temp != null) { | |
temp.remove(); | |
temp = null; | |
} | |
// Don't run if the owner isn't online! | |
if (!owner.isOnline()) { | |
return; | |
} | |
Player p = owner.getPlayer(); // Since we're certain the owner is | |
// online, we can use getPlayer(). | |
try { | |
temp = (LivingEntity) p.getWorld().spawnEntity(p.getLocation(), type); | |
} catch (Exception ex) { | |
// Spawn the entity as the default type if the type specified is not | |
// a LivingEntity. | |
temp = (LivingEntity) p.getWorld().spawnEntity(p.getLocation(), DEFAULT_TYPE); | |
} | |
tempUUID = temp.getUniqueId(); | |
if (!name.equals(NO_NAME)) { | |
temp.setCustomName(name); | |
temp.setCustomNameVisible(true); | |
} | |
setPetFollow(MAIN_CLASS, owner.getPlayer(), temp, 0.7f); | |
addToActiveList(tempUUID.toString(), false); | |
// TODO Let the player know that their pet is alive, maybe? | |
} | |
/** | |
* Remove the pet from memory. | |
*/ | |
public void cleanUp() { | |
HandlerList.unregisterAll(this); // Make sure we don't have extra | |
// Listeners! | |
if (temp != null) { | |
temp.remove(); // Get rid of the entity. | |
temp = null; // Set the entity to null so we're not storing it. | |
} | |
if (tempUUID != null) { | |
tempUUID = null; // Set the entity UUID to null so we're not storing | |
// it. | |
} | |
done = true; // Communicate with other methods/runnables. | |
if (pets.contains(this)) | |
pets.remove(this); // Remove the pet from the list so we're not | |
// storing a useless object. | |
} | |
@EventHandler | |
public void quit(PlayerQuitEvent e) { | |
if (e.getPlayer().getUniqueId().toString().equals(owner.getUniqueId().toString())) { | |
cleanUp(); // Call the cleanup method if the player isn't online. | |
} | |
} | |
@EventHandler | |
public void tp(PlayerTeleportEvent e) { | |
if (e.getPlayer().getUniqueId().toString().equals(owner.getUniqueId().toString())) { | |
spawn(); // Respawn the entity if the player teleports somewhere. | |
} | |
} | |
/** | |
* @return List of alive pets. | |
*/ | |
public static List<Pet> getAlivePets() { | |
return pets; | |
} | |
/** | |
* Call this in your main plugin's onDisable()! | |
*/ | |
public static void handleDisable() { | |
ArrayList<String> petUUIDs = new ArrayList<String>(); | |
for (Pet pet : pets) { | |
pet.cleanUp(); | |
} | |
pets.clear(); | |
} | |
/** | |
* Add an entity to the active list; incase the server loses track of the | |
* entities. | |
* | |
* @param name | |
* @throws FileNotFoundException | |
* if the file cannot be found. | |
*/ | |
private static ArrayList<String> addToActiveList(String name, boolean shouldClear) { | |
new File("./conf/pets/active/").mkdirs(); // Create the folders where | |
// the UUIDs will be stored. | |
// Should only be one per | |
// server. | |
ArrayList<String> names = new ArrayList<String>(); // List of lines in | |
// the file. | |
File f = new File("./conf/pets/active/active.db"); | |
if (!shouldClear || name == null) { | |
ArrayList<String> fLines = readFile(f); | |
if (fLines != null) | |
names.addAll(fLines); // Add the lines to the names list if the | |
// file | |
// already exists. | |
if (!names.contains(name)) { | |
names.add(name); | |
} | |
} | |
boolean deleteOldFile = true; // If we should delete the old file. | |
PrintWriter pw = null; | |
try { | |
pw = new PrintWriter(f); | |
} catch (FileNotFoundException e) { | |
deleteOldFile = false; // Keep the old file, we couldn't make the | |
// new one. | |
return names; // We're not going to be able to make a new file :( | |
} | |
if (deleteOldFile) { | |
f.delete(); // Delete the existing file, we'll recreate it in a | |
// moment. | |
} | |
for (String s : names) | |
pw.println(s); // Write each line in names to the file. | |
pw.close(); // Don't keep it open! | |
return names; // We're done here. | |
} | |
private static ArrayList<String> readFile(File f) { | |
if (!f.exists()) { | |
return null; // Something went wrong. | |
} | |
ArrayList<String> lines = new ArrayList<String>(); // List of lines in | |
// the file. | |
Scanner sc; // Create a scanner object to grab the info from | |
try { | |
sc = new Scanner(new FileInputStream(f)); | |
} catch (Exception ex) { | |
return null; // Something went wrong. | |
} | |
while (sc.hasNext()) { | |
lines.add(sc.nextLine()); | |
} | |
sc.close(); | |
return lines; | |
} | |
/** | |
* Force an entity to follow a player much like a pet wolf | |
* | |
* @param plugin | |
* the plugin forcing the entity to follow | |
* @param player | |
* the player that the 'pet' belongs to | |
* @param pet | |
* the entity that will act as a pet | |
* @param speed | |
* the speed that the entity will path at | |
*/ | |
private static setPetFollow(final Plugin plugin, final Player player, final LivingEntity pet, final double speed) { | |
new BukkitRunnable() { | |
public void run() { | |
if (done) { | |
cancel(); | |
} | |
if ((!pet.isValid() || (!player.isOnline()))) { | |
this.cancel(); | |
} | |
net.minecraft.server.v1_8_R1.Entity pett = ((CraftEntity) pet).getHandle(); | |
((EntityInsentient) pett).getNavigation().a(2); | |
Object petf = ((CraftEntity) pet).getHandle(); | |
Location targetLocation = player.getLocation(); | |
PathEntity path; | |
path = ((EntityInsentient) petf).getNavigation().a(targetLocation.getX() + 1, targetLocation.getY(), targetLocation.getZ() + 1); | |
if (path != null) { | |
((EntityInsentient) petf).getNavigation().a(path, 1.0D); | |
((EntityInsentient) petf).getNavigation().a(2.0D); | |
} | |
int distance = (int) Bukkit.getPlayer(player.getName()).getLocation().distance(pet.getLocation()); | |
if (distance > 10 && !pet.isDead()) { | |
pet.teleport(player.getLocation()); | |
} | |
AttributeInstance attributes = ((EntityInsentient) ((CraftEntity) pet).getHandle()).getAttributeInstance(GenericAttributes.d); | |
attributes.setValue(speed); | |
} | |
}.runTaskTimer(plugin, 0L, 20L); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment