Skip to content

Instantly share code, notes, and snippets.

@justinoboyle
Last active August 29, 2015 14:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justinoboyle/e5cb934f6c7b7027bf8c to your computer and use it in GitHub Desktop.
Save justinoboyle/e5cb934f6c7b7027bf8c to your computer and use it in GitHub Desktop.
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