From f52665348e3cf0e4896c9eb32c0e3c855a6d92e2 Mon Sep 17 00:00:00 2001
From: fullwall <fullwall@optusnet.com>
Date: Mon, 9 Jan 2012 15:51:32 +0800
Subject: [PATCH] [Bleeding] Added Potions API. Fixes BUKKIT-389

---
 .../net/minecraft/server/EntityLiving.java    |  2 +-
 .../net/minecraft/server/MobEffectList.java   |  4 +
 .../org/bukkit/craftbukkit/CraftServer.java   | 12 ++-
 .../craftbukkit/entity/CraftLivingEntity.java | 48 ++++++++++++
 .../craftbukkit/entity/CraftThrownPotion.java | 16 ++++
 .../craftbukkit/potion/CraftPotionBrewer.java | 47 ++++++++++++
 .../potion/CraftPotionEffectType.java         | 74 +++++++++++++++++++
 .../java/org/bukkit/potion/PotionTest.java    | 27 +++++++
 8 files changed, 227 insertions(+), 3 deletions(-)
 create mode 100644 src/main/java/org/bukkit/craftbukkit/potion/CraftPotionBrewer.java
 create mode 100644 src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
 create mode 100644 src/test/java/org/bukkit/potion/PotionTest.java

diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java
index d19bc0af7e..360605f4dd 100644
--- a/src/main/java/net/minecraft/server/EntityLiving.java
+++ b/src/main/java/net/minecraft/server/EntityLiving.java
@@ -63,7 +63,7 @@ public abstract class EntityLiving extends Entity {
     protected EntityLiving lastDamager = null;
     public int aJ = 0;
     public int aK = 0;
-    protected HashMap effects = new HashMap();
+    public HashMap effects = new HashMap(); // CraftBukkit - protected -> public
     private boolean b = true;
     private int c;
     private ControllerLook lookController;
diff --git a/src/main/java/net/minecraft/server/MobEffectList.java b/src/main/java/net/minecraft/server/MobEffectList.java
index 4d178290c5..ae27a5ac37 100644
--- a/src/main/java/net/minecraft/server/MobEffectList.java
+++ b/src/main/java/net/minecraft/server/MobEffectList.java
@@ -2,7 +2,9 @@ package net.minecraft.server;
 
 // CraftBukkit start
 import org.bukkit.Bukkit;
+import org.bukkit.potion.PotionEffectType;
 import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.craftbukkit.potion.CraftPotionEffectType;
 import org.bukkit.event.entity.EntityDamageEvent;
 import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
 import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
@@ -62,6 +64,8 @@ public class MobEffectList {
         }
 
         this.N = j;
+
+        PotionEffectType.registerPotionEffectType(new CraftPotionEffectType(this)); // CraftBukkit
     }
 
     protected MobEffectList a(int i, int j) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index caeb3844e3..ac018e97de 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -42,6 +42,7 @@ import net.minecraft.server.EntityPlayer;
 import net.minecraft.server.EntityTracker;
 import net.minecraft.server.IProgressUpdate;
 import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.MobEffectList;
 import net.minecraft.server.PropertyManager;
 import net.minecraft.server.ServerConfigurationManager;
 import net.minecraft.server.ServerNBTManager;
@@ -74,6 +75,7 @@ import org.bukkit.craftbukkit.inventory.CraftRecipe;
 import org.bukkit.craftbukkit.inventory.CraftShapedRecipe;
 import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe;
 import org.bukkit.craftbukkit.map.CraftMapView;
+import org.bukkit.craftbukkit.potion.CraftPotionBrewer;
 import org.bukkit.scheduler.BukkitWorker;
 import org.bukkit.craftbukkit.scheduler.CraftScheduler;
 import org.bukkit.craftbukkit.util.DatFileFilter;
@@ -82,6 +84,8 @@ import org.bukkit.util.permissions.DefaultPermissions;
 import org.bukkit.event.world.WorldInitEvent;
 import org.bukkit.permissions.Permission;
 import org.bukkit.plugin.PluginLoadOrder;
+import org.bukkit.potion.Potion;
+import org.bukkit.potion.PotionEffectType;
 import org.bukkit.plugin.messaging.StandardMessenger;
 import org.yaml.snakeyaml.Yaml;
 import org.yaml.snakeyaml.constructor.SafeConstructor;
@@ -114,9 +118,13 @@ public final class CraftServer implements Server {
 
         Bukkit.setServer(this);
 
-        // Register all the Enchantments now so we can stop new registration immediately after
+        // Register all the Enchantments and PotionTypes now so we can stop new registration immediately after
         Enchantment.DAMAGE_ALL.getClass();
         org.bukkit.enchantments.Enchantment.stopAcceptingRegistrations();
+
+        Potion.setPotionBrewer(new CraftPotionBrewer());
+        MobEffectList.BLINDNESS.getClass();
+        PotionEffectType.stopAcceptingRegistrations();
         // Ugly hack :(
 
         if (!Main.useConsole) {
@@ -565,7 +573,7 @@ public final class CraftServer implements Server {
         do {
             for (WorldServer server : console.worlds) {
                 used = server.dimension == dimension;
-               if (used) {
+                if (used) {
                     dimension++;
                     break;
                 }
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
index e1e8b20406..12a0deb0ca 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@@ -6,10 +6,13 @@ import net.minecraft.server.EntityEgg;
 import net.minecraft.server.EntityLiving;
 import net.minecraft.server.EntitySnowball;
 import net.minecraft.server.EntityPlayer;
+import net.minecraft.server.MobEffect;
+import net.minecraft.server.MobEffectList;
 
 import org.bukkit.Location;
 import org.bukkit.craftbukkit.CraftServer;
 import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.potion.CraftPotionEffectType;
 
 import org.bukkit.block.Block;
 import org.bukkit.entity.Arrow;
@@ -18,8 +21,11 @@ import org.bukkit.entity.LivingEntity;
 import org.bukkit.entity.Player;
 import org.bukkit.entity.Snowball;
 import org.bukkit.entity.Vehicle;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionEffectType;
 import org.bukkit.util.BlockIterator;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.HashSet;
 import java.util.ArrayList;
@@ -228,4 +234,46 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
     public Player getKiller() {
         return getHandle().killer == null ? null : (Player) getHandle().killer.getBukkitEntity();
     }
+
+    public boolean addPotionEffect(PotionEffect effect) {
+        return addPotionEffect(effect, false);
+    }
+
+    public boolean addPotionEffect(PotionEffect effect, boolean force) {
+        if (hasPotionEffect(effect.getType())) {
+            if (!force) {
+                return false;
+            }
+            removePotionEffect(effect.getType());
+        }
+        getHandle().addEffect(new MobEffect(effect.getType().getId(), effect.getDuration(), effect.getAmplifier()));
+        return true;
+    }
+
+    public boolean addPotionEffects(Collection<PotionEffect> effects) {
+        boolean success = true;
+        for (PotionEffect effect : effects) {
+            success &= addPotionEffect(effect);
+        }
+        return success;
+    }
+
+    public boolean hasPotionEffect(PotionEffectType type) {
+        return getHandle().hasEffect(MobEffectList.byId[type.getId()]);
+    }
+
+    public void removePotionEffect(PotionEffectType type) {
+        getHandle().effects.remove(type.getId());
+    }
+
+    public Collection<PotionEffect> getActivePotionEffects() {
+        List<PotionEffect> effects = new ArrayList<PotionEffect>();
+        for (Object raw : getHandle().effects.values()) {
+            if (!(raw instanceof MobEffect))
+                continue;
+            MobEffect handle = (MobEffect) raw;
+            effects.add(new PotionEffect(PotionEffectType.getById(handle.getEffectId()), handle.getDuration(), handle.getAmplifier()));
+        }
+        return effects;
+    }
 }
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java
index 81dca041ff..5afda66172 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftThrownPotion.java
@@ -1,14 +1,30 @@
 package org.bukkit.craftbukkit.entity;
 
+import java.util.Collection;
+
 import net.minecraft.server.EntityPotion;
+
 import org.bukkit.craftbukkit.CraftServer;
 import org.bukkit.entity.ThrownPotion;
+import org.bukkit.potion.Potion;
+import org.bukkit.potion.PotionEffectType;
+import org.bukkit.potion.PotionEffect;
 
 public class CraftThrownPotion extends CraftProjectile implements ThrownPotion {
+    private Collection<PotionEffect> effects = null;
+
     public CraftThrownPotion(CraftServer server, EntityPotion entity) {
         super(server, entity);
     }
 
+    public Collection<PotionEffect> getEffects() {
+        if (effects == null) {
+            effects = Potion.getBrewer().getEffectsFromDamage(getHandle().f());
+        }
+
+        return effects;
+    }
+
     @Override
     public EntityPotion getHandle() {
         return (EntityPotion) entity;
diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionBrewer.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionBrewer.java
new file mode 100644
index 0000000000..914c5cb384
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionBrewer.java
@@ -0,0 +1,47 @@
+package org.bukkit.craftbukkit.potion;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import net.minecraft.server.MobEffect;
+
+import org.bukkit.potion.PotionEffectType;
+import org.bukkit.potion.PotionBrewer;
+import org.bukkit.potion.PotionEffect;
+
+import com.google.common.collect.Maps;
+
+public class CraftPotionBrewer implements PotionBrewer {
+    private static final Map<Integer, Collection<PotionEffect>> cache = Maps.newHashMap();
+
+    public Collection<PotionEffect> getEffectsFromDamage(int damage) {
+        if (cache.containsKey(damage))
+            return cache.get(damage);
+
+        List<?> mcEffects = net.minecraft.server.PotionBrewer.a(damage, false);
+        List<PotionEffect> effects = new ArrayList<PotionEffect>();
+        if (mcEffects == null)
+            return effects;
+
+        for (Object raw : mcEffects) {
+            if (raw == null || !(raw instanceof MobEffect))
+                continue;
+            MobEffect mcEffect = (MobEffect) raw;
+            PotionEffect effect = new PotionEffect(PotionEffectType.getById(mcEffect.getEffectId()),
+                    mcEffect.getDuration(), mcEffect.getAmplifier());
+            // Minecraft PotionBrewer applies duration modifiers automatically.
+            effects.add(effect);
+        }
+
+        cache.put(damage, effects);
+
+        return effects;
+    }
+
+    public PotionEffect createEffect(PotionEffectType potion, int duration, int amplifier) {
+        return new PotionEffect(potion, potion.isInstant() ? 1 : (int) (duration * potion.getDurationModifier()),
+                amplifier);
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
new file mode 100644
index 0000000000..fabd9f72b1
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
@@ -0,0 +1,74 @@
+package org.bukkit.craftbukkit.potion;
+
+import net.minecraft.server.MobEffectList;
+
+import org.bukkit.potion.PotionEffectType;
+
+public class CraftPotionEffectType extends PotionEffectType {
+    private final MobEffectList handle;
+
+    public CraftPotionEffectType(MobEffectList handle) {
+        super(handle.id);
+        this.handle = handle;
+    }
+
+    @Override
+    public double getDurationModifier() {
+        return handle.d();
+    }
+
+    public MobEffectList getHandle() {
+        return handle;
+    }
+
+    @Override
+    public String getName() {
+        switch (handle.id) {
+        case 1:
+            return "SPEED";
+        case 2:
+            return "SLOW";
+        case 3:
+            return "FAST_DIGGING";
+        case 4:
+            return "SLOW_DIGGING";
+        case 5:
+            return "INCREASE_DAMAGE";
+        case 6:
+            return "HEAL";
+        case 7:
+            return "HARM";
+        case 8:
+            return "JUMP";
+        case 9:
+            return "CONFUSION";
+        case 10:
+            return "REGENERATION";
+        case 11:
+            return "DAMAGE_RESISTANCE";
+        case 12:
+            return "FIRE_RESISTANCE";
+        case 13:
+            return "WATER_BREATHING";
+        case 14:
+            return "INVISIBILITY";
+        case 15:
+            return "BLINDNESS";
+        case 16:
+            return "NIGHT_VISION";
+        case 17:
+            return "HUNGER";
+        case 18:
+            return "WEAKNESS";
+        case 19:
+            return "POISON";
+        default:
+            return "UNKNOWN_EFFECT_TYPE_" + handle.id;
+        }
+    }
+
+    @Override
+    public boolean isInstant() {
+        return handle.b();
+    }
+}
diff --git a/src/test/java/org/bukkit/potion/PotionTest.java b/src/test/java/org/bukkit/potion/PotionTest.java
new file mode 100644
index 0000000000..edc308f847
--- /dev/null
+++ b/src/test/java/org/bukkit/potion/PotionTest.java
@@ -0,0 +1,27 @@
+package org.bukkit.potion;
+
+import static org.junit.Assert.*;
+
+import org.bukkit.craftbukkit.potion.CraftPotionBrewer;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import net.minecraft.server.MobEffectList;
+
+public class PotionTest {
+    @BeforeClass
+    public static void setUp() {
+        Potion.setPotionBrewer(new CraftPotionBrewer());
+        MobEffectList.BLINDNESS.getClass();
+        PotionEffectType.stopAcceptingRegistrations();
+    }
+
+    @Test
+    public void getEffects() {
+        for (PotionType type : PotionType.values()) {
+            for (PotionEffect effect : new Potion(type).getEffects()) {
+                assertTrue(effect.getType() == PotionEffectType.getById(effect.getType().getId()));
+            }
+        }
+    }
+}