Browse Source

mapper, remaining snuvi functions, reworked sound / particle snuvi
functions, dynamic player cape loader, reflection hack to replace
default player renderer

Kajetan Johannes Hammerle 4 years ago
parent
commit
a93f62a365

+ 59 - 10
src/main/java/me/km/Client.java

@@ -2,6 +2,7 @@ package me.km;
 
 import java.awt.Color;
 import java.util.List;
+import java.util.Map;
 import me.km.blocks.ModBlocks;
 import me.km.blocks.cookingpot.TileEntityCookingPot;
 import me.km.items.ItemColoredSoup;
@@ -14,10 +15,16 @@ import me.km.networking.KeyManager;
 import me.km.networking.PlayerDisplayGui;
 import me.km.networking.PlayerHeadGui;
 import me.km.networking.StatusDisplayGui;
+import me.km.utils.ClientReflectionUtils;
 import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.screen.MainMenuScreen;
+import net.minecraft.client.gui.screen.MultiplayerScreen;
+import net.minecraft.client.gui.screen.Screen;
 import net.minecraft.client.renderer.color.BlockColors;
 import net.minecraft.client.renderer.color.IBlockColor;
 import net.minecraft.client.renderer.color.ItemColors;
+import net.minecraft.client.renderer.entity.EntityRenderer;
+import net.minecraft.entity.Entity;
 import net.minecraft.item.Item;
 import net.minecraft.item.ArmorItem;
 import net.minecraft.item.BlockItem;
@@ -31,6 +38,7 @@ import net.minecraft.world.GrassColors;
 import net.minecraft.world.biome.BiomeColors;
 import net.minecraftforge.api.distmarker.Dist;
 import net.minecraftforge.api.distmarker.OnlyIn;
+import net.minecraftforge.client.event.GuiScreenEvent;
 import net.minecraftforge.client.event.RenderGameOverlayEvent;
 import net.minecraftforge.common.MinecraftForge;
 import net.minecraftforge.event.entity.player.ItemTooltipEvent;
@@ -48,6 +56,7 @@ public class Client
         MinecraftForge.EVENT_BUS.register(new Client());
         MinecraftForge.EVENT_BUS.register(new KeyManager());
         registerColors();
+        ClientReflectionUtils.updatePlayerRenderer();
     }
     
     private static void registerColors()
@@ -118,16 +127,56 @@ public class Client
         }
     }
     
-    // ToDo: Event is missing?
-    //@SubscribeEvent
-    //public void onRenderGui(FMLNetworkEvent.ClientDisconnectionFromServerEvent e)
-    //{
-    //    PlayerDisplayGui.INSTANCE.clear();
-    //    StatusDisplayGui.INSTANCE.clear();
-    //    PlayerHeadGui.INSTANCE.clear();
-    //    ItemStackDisplayGui.INSTANCE.clear();
-    //    ItemStackDisplayGui.INSTANCE.setActive(false);
-    //}
+    /*private static class Data
+    {
+        private long[] time = new long[10];
+        private int index = 0;
+    }
+    
+    private static java.util.HashMap<Class, Data> testMap = new java.util.HashMap<>();
+    
+    @SubscribeEvent
+    public void test(net.minecraftforge.eventbus.api.Event e)
+    {
+        Class c = e.getClass();
+        Data data = testMap.get(c);
+        if(data != null && data.index >= 10)
+        {
+            if(data.time[9] - data.time[0] < 1_000_000_000)
+            {
+                return;
+            }
+            else
+            {
+                data.index = 0;
+            }
+        }
+        
+        if(data == null)
+        {
+            data = new Data();
+            testMap.put(c, data);
+        }
+        
+        data.time[data.index] = System.nanoTime();
+        data.index++;
+        org.apache.logging.log4j.LogManager.getLogger().warn(c);
+    }*/
+    
+    @SubscribeEvent
+    public void onRenderGui(GuiScreenEvent.InitGuiEvent e)
+    {
+        // workaround until a client side server leave event is back
+        Screen gui = e.getGui();
+        if(gui instanceof MainMenuScreen || gui instanceof MultiplayerScreen)
+        {
+            PlayerDisplayGui.INSTANCE.clear();
+            StatusDisplayGui.INSTANCE.clear();
+            PlayerHeadGui.INSTANCE.clear();
+            ItemStackDisplayGui.INSTANCE.clear();
+            ItemStackDisplayGui.INSTANCE.setActive(false);
+        }
+    }
     
     @SubscribeEvent
     public void showingTooltip(ItemTooltipEvent e) 

+ 0 - 209
src/main/java/me/km/effects/EffectUtils.java

@@ -1,209 +0,0 @@
-package me.km.effects;
-
-import me.km.KajetansMod;
-import me.km.utils.Utils;
-import me.km.entities.EntityItemProjectile;
-import net.minecraft.entity.Entity;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.entity.item.EnderPearlEntity;
-import net.minecraft.entity.item.ExperienceBottleEntity;
-import net.minecraft.entity.player.PlayerEntity;
-import net.minecraft.entity.player.ServerPlayerEntity;
-import net.minecraft.entity.projectile.*;
-import net.minecraft.inventory.EquipmentSlotType;
-import net.minecraft.item.Items;
-import net.minecraft.item.ItemStack;
-import net.minecraft.particles.IParticleData;
-import net.minecraft.potion.Effect;
-import net.minecraft.potion.EffectInstance;
-import net.minecraft.potion.Potion;
-import net.minecraft.potion.PotionUtils;
-import net.minecraft.util.SoundCategory;
-import net.minecraft.util.SoundEvent;
-import net.minecraft.util.datafix.fixes.TippedArrow;
-import net.minecraft.util.math.Vec3d;
-import net.minecraft.world.World;
-import net.minecraft.world.ServerWorld;
-
-public class EffectUtils
-{
-    // -----------------------------------------------------------------------------------
-    // Particles
-    // -----------------------------------------------------------------------------------
-
-    public static void spawnParticle(ServerWorld w, IParticleData particle, double x, double y, double z, double offX, double offY, double offZ, int count, int... data)
-    {
-        //w.spawnParticle(particle, false, x, y, z, count, offX, offY, offZ, 1, data);
-    }
-
-    public static void spawnParticle(ServerWorld w, IParticleData particle, double x, double y, double z, double offX, double offY, double offZ, int count)
-    {
-        spawnParticle(w, particle, x, y, z, offX, offY, offZ, count, new int[0]);
-    }
-
-    public static void spawnParticle(ServerWorld w, IParticleData particle, double x, double y, double z, int count)
-    {
-        spawnParticle(w, particle, x, y, z, 0, 0, 0, count);
-    }
-
-    public static void spawnParticle(ServerWorld w, IParticleData particle, Entity ent, int count)
-    {
-        spawnParticle(w, particle, ent.posX, ent.posY, ent.posZ, count);
-    }
-
-    public static void spawnParticleCircle(ServerWorld w, double x, double y, double z, IParticleData particle, double radius, int counter, int... data)
-    {
-        double angle = 2 * Math.PI / counter;
-        for(int i = 0; i < counter; i++)
-        {
-            spawnParticle(w, particle, x + Math.cos(i * angle) * radius, y, z + Math.sin(i * angle) * radius, 0, 0, 0, 1, data);
-        }
-    }
-
-    public static void spawnParticleCircle(ServerWorld w, Entity ent, IParticleData particle, double radius, int counter, int... data)
-    {
-        spawnParticleCircle(w, ent.posX, ent.posY, ent.posZ, particle, radius, counter, data);
-    }
-
-    public static void spawnParticleCircle(ServerWorld w, double x, double y, double z, IParticleData particle, double radius, int counter)
-    {
-        spawnParticleCircle(w, x, y, z, particle, radius, counter, new int[0]);
-    }
-
-    public static void spawnParticleCircle(ServerWorld w, Entity ent, IParticleData particle, double radius, int counter)
-    {
-        spawnParticleCircle(w, ent.posX, ent.posY, ent.posZ, particle, radius, counter, new int[0]);
-    }
-
-    // -----------------------------------------------------------------------------------
-    // Sounds
-    // -----------------------------------------------------------------------------------
-    public static void playSound(World w, SoundEvent se, SoundCategory sc, double x, double y, double z)
-    {
-        w.playSound(null, x, y, z, se, sc, 1, w.rand.nextFloat() * 0.1f + 0.9f);
-    }
-
-    public static void playSound(PlayerEntity p, SoundEvent se, SoundCategory sc)
-    {
-        playSound(p.world, se, sc, p.posX, p.posY, p.posZ);
-    }
-
-    public static void playSound(PlayerEntity p, SoundEvent se)
-    {
-        playSound(p.world, se, SoundCategory.PLAYERS, p.posX, p.posY, p.posZ);
-    }
-
-    // -----------------------------------------------------------------------------------
-    // Potions
-    // -----------------------------------------------------------------------------------
-    public static void addPotionTo(LivingEntity ent, Effect potion, int duration, int amplifier)
-    {
-        if(ent.isPotionActive(potion))
-        {
-            ent.removePotionEffect(potion);
-        }
-        ent.addPotionEffect(new EffectInstance(potion, duration, amplifier));
-    }
-
-    // -----------------------------------------------------------------------------------
-    // Projectiles
-    // -----------------------------------------------------------------------------------
-    public static <T> T launchProjectile(PlayerEntity p, Class<? extends T> projectile, double scale, Object data)
-    {
-        World w = p.world;
-        Entity launch = null;
-
-        if(EntityItemProjectile.class == projectile)
-        {
-            if(data == null)
-            {
-                throw new NullPointerException("Data musn't be null for EntityItemProjectile");
-            }
-            ItemStack stack = (ItemStack) data;
-            if(stack.isEmpty())
-            {
-                throw new IllegalArgumentException("Empty ItemStack not allowed here");
-            }
-            launch = new EntityItemProjectile(p, stack.copy());
-            ((EntityItemProjectile) launch).setHeadingFromThrower(p, p.rotationPitch, p.rotationYaw, 0.0f, 1.5f, 1.0f);
-        }
-        else if(SnowballEntity.class == projectile)
-        {
-            launch = new SnowballEntity(w, p);
-            ((SnowballEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, 0.0f, 1.5f, 1.0f);
-        }
-        else if(EggEntity.class == projectile)
-        {
-            launch = new EggEntity(w, p);
-            ((EggEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, 0.0f, 1.5f, 1.0f);
-        }
-        else if(EnderPearlEntity.class == projectile)
-        {
-            launch = new EnderPearlEntity(w, p);
-            ((EnderPearlEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, 0.0f, 1.5f, 1.0f);
-        }
-        else if(PotionEntity.class == projectile)
-        {
-            launch = new PotionEntity(w, p);
-            ((PotionEntity) launch).setItem((ItemStack) data);
-            ((PotionEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, -20.0f, 0.5f, 1.0f);
-        }
-        else if(ExperienceBottleEntity.class == projectile)
-        {
-            launch = new ExperienceBottleEntity(w, p);
-            ((ExperienceBottleEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, -20.0f, 0.7f, 1.0f);
-        }
-        else if(AbstractArrowEntity.class.isAssignableFrom(projectile))
-        {
-            if(SpectralArrowEntity.class == projectile)
-            {
-                launch = new SpectralArrowEntity(w, p);
-            }
-            else
-            {
-                launch = new ArrowEntity(w, p);
-                if(data != null)
-                {
-                    ((ArrowEntity) launch).setPotionEffect((ItemStack) data);
-                }
-            }
-            ((AbstractArrowEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, 0.0F, 3.0F, 1.0F);
-        }
-        else if(DamagingProjectileEntity.class.isAssignableFrom(projectile))
-        {
-            Vec3d v = p.getLookVec().scale(10);
-            if(SmallFireballEntity.class == projectile)
-            {
-                launch = new SmallFireballEntity(w, p, v.x, v.y, v.z);
-            }
-            else if(WitherSkullEntity.class == projectile)
-            {
-                launch = new WitherSkullEntity(w, p, v.x, v.y, v.z);
-            }
-            else if(DragonFireballEntity.class == projectile)
-            {
-                launch = new DragonFireballEntity(w, p, v.x, v.y, v.z);
-            }
-            else
-            {
-                launch = new FireballEntity(w, p, v.x, v.y, v.z);
-            }
-        }
-        else
-        {
-            return null;
-        }
-
-        launch.setMotion(launch.getMotion().scale(scale));
-        w.addEntity(launch);
-        return (T) launch;
-    }
-
-    public static ArrowEntity launchTippedArrow(PlayerEntity p, double scale, Effect potion, int duration, int amplifier)
-    {
-        ItemStack stack = new ItemStack(Items.TIPPED_ARROW);
-        PotionUtils.addPotionToItemStack(stack, new Potion(new EffectInstance(potion, duration, amplifier)));
-        ArrowEntity arrow = launchProjectile(p, ArrowEntity.class, scale, stack);
-        return arrow;
-    }
-}

+ 13 - 14
src/main/java/me/km/entities/HumanSkinLoader.java

@@ -2,7 +2,6 @@ package me.km.entities;
 
 import java.net.HttpURLConnection;
 import java.net.URL;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.function.Consumer;
@@ -20,19 +19,19 @@ import org.apache.logging.log4j.LogManager;
 public class HumanSkinLoader
 {
     public final static HumanSkinLoader INSTANCE = new HumanSkinLoader();
-    
+
     private static final ResourceLocation TEXTURE_STEVE = new ResourceLocation("textures/entity/steve.png");
-    
+
     private final HashMap<String, ResourceLocation> skins = new HashMap<>();
     private final HashSet<String> loading = new HashSet<>();
-    
+
     private final TextureManager manager = Minecraft.getInstance().getRenderManager().textureManager;
     private final DownloadImageBuffer converter = new DownloadImageBuffer();
-    
-    public HumanSkinLoader()
+
+    private HumanSkinLoader()
     {
     }
-    
+
     public ResourceLocation getTexture(String name)
     {
         ResourceLocation loc = skins.get(name);
@@ -42,7 +41,7 @@ public class HumanSkinLoader
         }
         if(loading.add(name))
         {
-            downloadSkin(name, image -> 
+            downloadSkin(name, image ->
             {
                 ResourceLocation rloc = manager.getDynamicTextureLocation("skin_" + name, new DynamicTexture(image));
                 skins.put(name, rloc);
@@ -51,10 +50,10 @@ public class HumanSkinLoader
         }
         return TEXTURE_STEVE;
     }
-    
+
     private void downloadSkin(String name, Consumer<NativeImage> delayed)
     {
-        new Thread(() -> 
+        new Thread(() ->
         {
             HttpURLConnection httpurlconnection = null;
             try
@@ -83,21 +82,21 @@ public class HumanSkinLoader
                         return;
                     }
                 }
-                
+
                 NativeImage image = converter.parseUserSkin(NativeImage.read(httpurlconnection.getInputStream()));
                 Minecraft.getInstance().enqueue(() -> delayed.accept(image));
             }
-            catch (Exception ex)
+            catch(Exception ex)
             {
                 LogManager.getLogger().warn("Error occurred when downloading skin, however, skin servers seem to be up.");
             }
             finally
             {
-                if (httpurlconnection != null)
+                if(httpurlconnection != null)
                 {
                     httpurlconnection.disconnect();
                 }
             }
         }).start();
     }
-}
+}

+ 77 - 0
src/main/java/me/km/overrides/ModCapeLayer.java

@@ -0,0 +1,77 @@
+package me.km.overrides;
+
+import com.mojang.blaze3d.platform.GlStateManager;
+import me.km.KajetansMod;
+import net.minecraft.client.entity.player.AbstractClientPlayerEntity;
+import net.minecraft.client.renderer.entity.IEntityRenderer;
+import net.minecraft.client.renderer.entity.layers.CapeLayer;
+import net.minecraft.client.renderer.entity.model.PlayerModel;
+import net.minecraft.entity.player.PlayerModelPart;
+import net.minecraft.inventory.EquipmentSlotType;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.MathHelper;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+
+@OnlyIn(Dist.CLIENT)
+public class ModCapeLayer extends CapeLayer
+{
+    public ModCapeLayer(IEntityRenderer<AbstractClientPlayerEntity, PlayerModel<AbstractClientPlayerEntity>> pRenderer)
+    {
+        super(pRenderer);
+    }
+
+    @Override
+    public void render(AbstractClientPlayerEntity entityIn, float p_212842_2_, float p_212842_3_, float p_212842_4_, float p_212842_5_, float p_212842_6_, float p_212842_7_, float p_212842_8_)
+    {
+        if(entityIn.hasPlayerInfo() && !entityIn.isInvisible() && entityIn.isWearing(PlayerModelPart.CAPE))
+        {
+            ItemStack itemstack = entityIn.getItemStackFromSlot(EquipmentSlotType.CHEST);
+            if(itemstack.getItem() != Items.ELYTRA)
+            {
+                ResourceLocation rl = PlayerCapeLoader.INSTANCE.getTexture(entityIn.getUniqueID());
+                if(rl == null)
+                {
+                    return;
+                }
+                
+                GlStateManager.color4f(1.0F, 1.0F, 1.0F, 1.0F);
+                this.bindTexture(rl);
+                GlStateManager.pushMatrix();
+                GlStateManager.translatef(0.0F, 0.0F, 0.125F);
+                double d0 = MathHelper.lerp((double) p_212842_4_, entityIn.prevChasingPosX, entityIn.chasingPosX) - MathHelper.lerp((double) p_212842_4_, entityIn.prevPosX, entityIn.posX);
+                double d1 = MathHelper.lerp((double) p_212842_4_, entityIn.prevChasingPosY, entityIn.chasingPosY) - MathHelper.lerp((double) p_212842_4_, entityIn.prevPosY, entityIn.posY);
+                double d2 = MathHelper.lerp((double) p_212842_4_, entityIn.prevChasingPosZ, entityIn.chasingPosZ) - MathHelper.lerp((double) p_212842_4_, entityIn.prevPosZ, entityIn.posZ);
+                float f = entityIn.prevRenderYawOffset + (entityIn.renderYawOffset - entityIn.prevRenderYawOffset);
+                double d3 = (double) MathHelper.sin(f * ((float) Math.PI / 180F));
+                double d4 = (double) (-MathHelper.cos(f * ((float) Math.PI / 180F)));
+                float f1 = (float) d1 * 10.0F;
+                f1 = MathHelper.clamp(f1, -6.0F, 32.0F);
+                float f2 = (float) (d0 * d3 + d2 * d4) * 100.0F;
+                f2 = MathHelper.clamp(f2, 0.0F, 150.0F);
+                float f3 = (float) (d0 * d4 - d2 * d3) * 100.0F;
+                f3 = MathHelper.clamp(f3, -20.0F, 20.0F);
+                if(f2 < 0.0F)
+                {
+                    f2 = 0.0F;
+                }
+
+                float f4 = MathHelper.lerp(p_212842_4_, entityIn.prevCameraYaw, entityIn.cameraYaw);
+                f1 = f1 + MathHelper.sin(MathHelper.lerp(p_212842_4_, entityIn.prevDistanceWalkedModified, entityIn.distanceWalkedModified) * 6.0F) * 32.0F * f4;
+                if(entityIn.func_213287_bg())
+                {
+                    f1 += 25.0F;
+                }
+
+                GlStateManager.rotatef(6.0F + f2 / 2.0F + f1, 1.0F, 0.0F, 0.0F);
+                GlStateManager.rotatef(f3 / 2.0F, 0.0F, 0.0F, 1.0F);
+                GlStateManager.rotatef(-f3 / 2.0F, 0.0F, 1.0F, 0.0F);
+                GlStateManager.rotatef(180.0F, 0.0F, 1.0F, 0.0F);
+                this.getEntityModel().renderCape(0.0625F);
+                GlStateManager.popMatrix();
+            }
+        }
+    }
+}

+ 12 - 0
src/main/java/me/km/overrides/ModPlayerList.java

@@ -42,6 +42,7 @@ import net.minecraft.server.management.PlayerList;
 import net.minecraft.server.management.PlayerProfileCache;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.math.Vec3d;
+import net.minecraft.util.text.ITextComponent;
 import net.minecraft.util.text.TranslationTextComponent;
 import net.minecraft.world.IWorld;
 import net.minecraft.world.ServerWorld;
@@ -330,4 +331,15 @@ public class ModPlayerList extends DedicatedPlayerList
     {
         ReflectionUtils.setPlayerGameTypeBasedOnOther(this, target, source, w);
     }
+
+    @Override
+    public void sendMessage(ITextComponent component)
+    {
+        // filter multiplayer leave text
+        if(component instanceof TranslationTextComponent && ((TranslationTextComponent) component).getKey().equals("multiplayer.player.left"))
+        {
+            return;
+        }
+        super.sendMessage(component);
+    }
 }

+ 16 - 0
src/main/java/me/km/overrides/ModPlayerRenderer.java

@@ -0,0 +1,16 @@
+package me.km.overrides;
+
+import net.minecraft.client.renderer.entity.EntityRendererManager;
+import net.minecraft.client.renderer.entity.PlayerRenderer;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+
+@OnlyIn(Dist.CLIENT)
+public class ModPlayerRenderer extends PlayerRenderer
+{
+    public ModPlayerRenderer(EntityRendererManager renderManager, boolean useSmallArms)
+    {
+        super(renderManager, useSmallArms);
+        this.addLayer(new ModCapeLayer(this));
+    }
+}

+ 87 - 0
src/main/java/me/km/overrides/PlayerCapeLoader.java

@@ -0,0 +1,87 @@
+package me.km.overrides;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.UUID;
+import java.util.function.Consumer;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.texture.DynamicTexture;
+import net.minecraft.client.renderer.texture.NativeImage;
+import net.minecraft.client.renderer.texture.TextureManager;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.api.distmarker.Dist;
+import net.minecraftforge.api.distmarker.OnlyIn;
+import org.apache.logging.log4j.LogManager;
+
+@OnlyIn(Dist.CLIENT)
+public class PlayerCapeLoader
+{
+    private static class Data
+    {
+        private ResourceLocation cape;
+    }
+
+    public final static PlayerCapeLoader INSTANCE = new PlayerCapeLoader();
+
+    private final HashMap<UUID, Data> skins = new HashMap<>();
+
+    private final TextureManager manager = Minecraft.getInstance().getRenderManager().textureManager;
+
+    private PlayerCapeLoader()
+    {
+    }
+
+    public ResourceLocation getTexture(UUID uuid)
+    {
+        Data oldData = skins.get(uuid);
+        if(oldData != null)
+        {
+            return oldData.cape;
+        }
+        Data newData = new Data();
+        skins.put(uuid, newData);
+        downloadCape(uuid, image ->
+        {
+            newData.cape = manager.getDynamicTextureLocation("cape_" + uuid, new DynamicTexture(image));
+        });
+        return null;
+    }
+
+    private void downloadCape(UUID uuid, Consumer<NativeImage> delayed)
+    {
+        new Thread(() ->
+        {
+            HttpURLConnection httpurlconnection = null;
+            try
+            {
+                org.apache.logging.log4j.LogManager.getLogger().warn(String.format("http://skins.hammerle.me/cape_%s.png", uuid));
+                httpurlconnection = (HttpURLConnection) (new URL("http://skins.hammerle.me/cape.png"))
+                        .openConnection(Minecraft.getInstance().getProxy());
+                httpurlconnection.setDoInput(true);
+                httpurlconnection.setDoOutput(false);
+                httpurlconnection.connect();
+
+                int code = httpurlconnection.getResponseCode();
+                if(code != 200)
+                {
+                    return;
+                }
+
+                NativeImage image = NativeImage.read(httpurlconnection.getInputStream());
+                Minecraft.getInstance().enqueue(() -> delayed.accept(image));
+            }
+            catch(Exception ex)
+            {
+                LogManager.getLogger().warn("Error occurred when downloading skin, however, skin servers seem to be up.");
+            }
+            finally
+            {
+                if(httpurlconnection != null)
+                {
+                    httpurlconnection.disconnect();
+                }
+            }
+        }).start();
+    }
+}

+ 246 - 160
src/main/java/me/km/snuviscript/MinecraftFunctions.java

@@ -21,8 +21,8 @@ import me.km.databank.DataBank;
 import me.km.utils.Location;
 import me.km.utils.Utils;
 import me.km.world.WorldManager;
-import me.km.effects.EffectUtils;
 import me.km.entities.EntityHuman;
+import me.km.entities.EntityItemProjectile;
 import me.km.entities.ModEntities;
 import me.km.inventory.InventoryUtils;
 import me.km.utils.ItemStackUtils;
@@ -61,19 +61,35 @@ import net.minecraft.command.arguments.BlockStateParser;
 import net.minecraft.entity.AgeableEntity;
 import net.minecraft.entity.EntityType;
 import net.minecraft.entity.LivingEntity;
+import net.minecraft.entity.MobEntity;
 import net.minecraft.entity.SharedMonsterAttributes;
+import net.minecraft.entity.SpawnReason;
 import net.minecraft.entity.ai.attributes.AttributeModifier.Operation;
+import net.minecraft.entity.effect.LightningBoltEntity;
+import net.minecraft.entity.item.EnderPearlEntity;
+import net.minecraft.entity.item.ExperienceBottleEntity;
 import net.minecraft.entity.item.ItemEntity;
 import net.minecraft.entity.item.ItemFrameEntity;
 import net.minecraft.entity.monster.CreeperEntity;
 import net.minecraft.entity.player.PlayerEntity;
 import net.minecraft.entity.player.ServerPlayerEntity;
+import net.minecraft.entity.projectile.AbstractArrowEntity;
+import net.minecraft.entity.projectile.ArrowEntity;
+import net.minecraft.entity.projectile.DamagingProjectileEntity;
+import net.minecraft.entity.projectile.DragonFireballEntity;
+import net.minecraft.entity.projectile.EggEntity;
+import net.minecraft.entity.projectile.FireballEntity;
 import net.minecraft.entity.projectile.PotionEntity;
+import net.minecraft.entity.projectile.SmallFireballEntity;
+import net.minecraft.entity.projectile.SnowballEntity;
+import net.minecraft.entity.projectile.SpectralArrowEntity;
+import net.minecraft.entity.projectile.WitherSkullEntity;
 import net.minecraft.inventory.EquipmentSlotType;
 import net.minecraft.item.ArmorItem;
 import net.minecraft.item.Item;
 import net.minecraft.item.Items;
 import net.minecraft.nbt.CompoundNBT;
+import net.minecraft.nbt.JsonToNBT;
 import net.minecraft.nbt.NBTUtil;
 import net.minecraft.network.play.client.CChatMessagePacket;
 import net.minecraft.network.play.client.CClientStatusPacket;
@@ -81,6 +97,12 @@ import net.minecraft.network.play.server.SEntityVelocityPacket;
 import net.minecraft.network.play.server.SSpawnPositionPacket;
 import net.minecraft.network.play.server.STitlePacket;
 import net.minecraft.network.play.server.SUpdateTileEntityPacket;
+import net.minecraft.particles.BlockParticleData;
+import net.minecraft.particles.IParticleData;
+import net.minecraft.particles.ItemParticleData;
+import net.minecraft.particles.ParticleType;
+import net.minecraft.particles.ParticleTypes;
+import net.minecraft.particles.RedstoneParticleData;
 import net.minecraft.potion.Effect;
 import net.minecraft.potion.EffectInstance;
 import net.minecraft.server.MinecraftServer;
@@ -97,6 +119,7 @@ import net.minecraft.tileentity.SkullTileEntity;
 import net.minecraft.util.Direction;
 import net.minecraft.util.NonNullList;
 import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.SoundEvent;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.text.ITextComponent;
 import net.minecraft.util.text.StringTextComponent;
@@ -227,7 +250,8 @@ public class MinecraftFunctions
             stack.setCount(InventoryUtils.addToInventory(((PlayerEntity) in[0].get(sc)).inventory, stack));
             return stack;
         });
-        sm.registerFunction("player.shootprojectile", (sc, in) -> EffectUtils.launchProjectile((PlayerEntity) in[0].get(sc), getClass(in[1].getString(sc)), in[2].getDouble(sc), in[3].get(sc)));
+        sm.registerFunction("player.shootprojectile", (sc, in) -> launchProjectile((PlayerEntity) in[0].get(sc), 
+                getClass(in[1].getString(sc)), in[2].getDouble(sc), in.length >= 4 ? in[3].get(sc) : null));
         sm.registerFunction("player.respawn", (sc, in) -> 
         {
             final ServerPlayerEntity p = ((ServerPlayerEntity) in[0].get(sc));
@@ -402,11 +426,6 @@ public class MinecraftFunctions
             return Void.TYPE; 
         }); 
         sm.registerFunction("player.kick", (sc, in) -> scripts.unregisterPlayer(sc, (PlayerEntity) in[0].get(sc)));  
-        sm.registerFunction("player.playsound", (sc, in) -> 
-        { 
-            EffectUtils.playSound((ServerPlayerEntity) in[0].get(sc), Mapper.getSound(in[1].getString(sc)), SoundCategory.MASTER);
-            return Void.TYPE; 
-        });
         sm.registerFunction("player.getspawn", (sc, in) -> 
         {   
             ServerWorld ws = (ServerWorld) in[1].get(sc);
@@ -501,52 +520,6 @@ public class MinecraftFunctions
             return Utils.getPlayers(l.getWorld(), l.getX(), l.getY(), l.getZ(), in[1].getDouble(sc)); 
         });
         
-        // ---------------------------------------------------------------------    
-        // Custom-library
-        // ---------------------------------------------------------------------
-        /*parser.registerFunction("custom.registershapelessrecipe", (sc, in) -> 
-        { 
-            ItemStack[] stacks = new ItemStack[in.length - 1];
-            for(int i = 0; i < stacks.length; i++)
-            {
-                stacks[i] = (ItemStack) in[i + 1].get(sc);
-            }
-            RecipeUtils.registerShapelessRecipe((ItemStack) in[0].get(sc), stacks); 
-            return Void.TYPE; 
-        });
-        parser.registerFunction("custom.registershapedrecipe", (sc, in) -> 
-        { 
-            int counter = 0;
-            while(in[counter + 1].get(sc).getClass() == String.class)
-            {
-                counter++;
-            }
-            String[] s = new String[counter];
-            for(int i = 0; i < s.length; i++)
-            {
-                s[i] = in[i + 1].toString();
-            }
-
-            ItemStack[] stacks = new ItemStack[in.length - 1 - s.length];
-            for(int i = 0; i < stacks.length; i++)
-            {
-                stacks[i] = (ItemStack) in[i + 1 + counter].get(sc);
-            }
-            RecipeUtils.registerShapedRecipe((ItemStack) in[0].get(sc), s, stacks); 
-            return Void.TYPE; 
-        });
-        parser.registerFunction("custom.registerfurnacerecipe", (sc, in) -> 
-        {
-            RecipeUtils.registerFurnaceRecipe((ItemStack) in[0].get(sc), (ItemStack) in[1].get(sc)); 
-            return Void.TYPE; 
-        });
-        parser.registerFunction("custom.clearrecipes", (sc, in) -> 
-        {
-            RecipeUtils.clearRecipes(); 
-            return Void.TYPE; 
-        });*/
-        
-        
         // ---------------------------------------------------------------------    
         // World-library
         // ---------------------------------------------------------------------
@@ -746,7 +719,6 @@ public class MinecraftFunctions
         { 
             ItemStack stack = (ItemStack) in[0].get(sc);                  
             int value = in[1].getInt(sc);  
-            System.out.println(stack.getItem());
             if(stack.getItem() instanceof ArmorItem)
             {
                 CompoundNBT com = stack.getTag();
@@ -1358,15 +1330,15 @@ public class MinecraftFunctions
         { 
             LivingEntity base = (LivingEntity) in[0].get(sc);
             Effect potion = Mapper.getPotion(in[1].getString(sc));
-            if(base == null) // doing this only to prevent EffectUtils.addPotionTo doing shit
+            if(potion == null) // doing this only to prevent EffectInstance doing shit
             {
-                throw new NullPointerException("null not allowed for entity");
+                throw new IllegalArgumentException("potion does not exist");
             }
-            else if(potion == null)
+            if(base.isPotionActive(potion))
             {
-                throw new NullPointerException("potion does not exist");
+                base.removePotionEffect(potion);
             }
-            EffectUtils.addPotionTo(base, potion, in[2].getInt(sc), in[3].getInt(sc));
+            base.addPotionEffect(new EffectInstance(potion, in[2].getInt(sc), in[3].getInt(sc)));
             return Void.TYPE; 
         });
         sm.registerFunction("entity.cleareffects", (sc, in) -> 
@@ -1385,6 +1357,7 @@ public class MinecraftFunctions
             Location l = ((Location) in[0].get(sc));
             ItemFrameEntity frame = new ItemFrameEntity(l.getWorld().getWorld(), l.getBlockPos(), Direction.byName(in[1].getString(sc)));
             frame.setDisplayedItem(((ItemStack) in[2].get(sc))); // copy happens in internals
+            l.getWorld().addEntity(frame);
             return Void.TYPE; 
         });
         sm.registerFunction("entity.getitemframe", (sc, in) -> ((ItemFrameEntity) in[0].get(sc)).getDisplayedItem());
@@ -1412,8 +1385,29 @@ public class MinecraftFunctions
         });
         sm.registerFunction("entity.spawn", (sc, in) -> 
         { 
-            // Todo
-            return null;
+            ResourceLocation rl = new ResourceLocation(in[0].getString(sc));
+            Location l = (Location) in[1].get(sc);
+            ServerWorld sw = (ServerWorld) l.getWorld();
+
+            CompoundNBT compoundnbt = in.length >= 3 ? JsonToNBT.getTagFromJson(in[2].getString(sc)) : new CompoundNBT(); 
+            compoundnbt.putString("id", rl.toString());
+            if(EntityType.getKey(EntityType.LIGHTNING_BOLT).equals(rl))
+            {
+                LightningBoltEntity ent = new LightningBoltEntity(sw, l.getX(), l.getY(), l.getZ(), false);
+                sw.addLightningBolt(ent);
+                return ent;
+            }
+
+            Entity entity = EntityType.func_220335_a(compoundnbt, sw, (ent) ->
+            {
+                ent.setLocationAndAngles(l.getX(), l.getY(), l.getZ(), ent.rotationYaw, ent.rotationPitch);
+                return sw.summonEntity(ent) ? ent : null;
+            });
+            if(entity != null && entity instanceof MobEntity)
+            {
+                ((MobEntity) entity).onInitialSpawn(sw, sw.getDifficultyForLocation(new BlockPos(entity)), SpawnReason.COMMAND, null, null);
+            }
+            return entity;
         });
         sm.registerFunction("entity.near", (sc, in) -> Utils.getLiving((Entity) in[0].get(sc), in[1].getDouble(sc)));
         sm.registerFunction("entity.setspeed", (sc, in) -> 
@@ -1454,73 +1448,7 @@ public class MinecraftFunctions
             ((EntityHuman) in[0].get(sc)).setSlim(in[1].getBoolean(sc));   
             return Void.TYPE; 
         });
-        
-        // ---------------------------------------------------------------------
-        // villager commands
-        // ---------------------------------------------------------------------
-        //sm.registerFunction("villager.showtrades", (sc, in) -> 
-        //{ 
-        //    ((ServerPlayerEntity) in[0].get(sc)).displayVillagerTradeGui(((EntityVillager) in[1].get(sc))); 
-        //    return Void.TYPE; 
-        //});
-        //sm.registerFunction("villager.cleartrades", (sc, in) -> 
-        //{ 
-        //    ((VillagerEntity) in[0].get(sc)).getRecipes(null).clear();
-        //    return Void.TYPE; 
-        //});
-        //sm.registerFunction("villager.addtrade", (sc, in) -> 
-        //{ 
-        //    EntityVillager v = (EntityVillager) in[0].get(sc);
-        //    ReflectionUtils.setCareerLevel(v, 10);
-        //    ItemStack buy1 = (ItemStack) in[1].get(sc);
-        //    if(buy1 == null)
-        //    {
-        //        buy1 = ItemStack.EMPTY;
-        //    }
-        //    ItemStack buy2 = (ItemStack) in[2].get(sc);
-        //    if(buy2 == null)
-        //    {
-        //        buy2 = ItemStack.EMPTY;
-        //    }
-        //    ItemStack sell = (ItemStack) in[3].get(sc);
-        //    if(buy2 == null)
-        //    {
-        //        buy2 = ItemStack.EMPTY;
-        //    }
-        //    MerchantRecipe mr = new MerchantRecipe(buy1, buy2, sell, 0, Integer.MAX_VALUE);
-        //    if(in.length > 4)
-        //    {
-        //        v.getRecipes(null).set(in[4].getInt(sc), mr);
-        //    }
-        //    else
-        //    {
-        //        v.getRecipes(null).add(mr);
-        //    }
-        //    return Void.TYPE; 
-        //});
-        //sm.registerFunction("villager.removetrade", (sc, in) -> 
-        //{ 
-        //    EntityVillager v = (EntityVillager) in[0].get(sc);
-        //    v.getRecipes(null).remove(in[1].get(sc));
-        //    return Void.TYPE; 
-        //});
-        //sm.registerFunction("villager.tradeamount", (sc, in) -> ((EntityVillager) in[0].get(sc)).getRecipes(null).size());
-        //sm.registerFunction("villager.getslot", (sc, in) -> ((EntityVillager) in[0].get(sc)).getVillagerInventory().getStackInSlot(in[1].getInt(sc)));
-        //sm.registerFunction("villager.setslot", (sc, in) -> 
-        //{
-        //    ((EntityVillager) in[0].get(sc)).getVillagerInventory().setInventorySlotContents(in[1].getInt(sc), ((ItemStack) in[2].get(sc)).copy());
-        //    return Void.TYPE; 
-        //});
-        //sm.registerFunction("villager.spawn", (sc, in) -> 
-        //{
-        //    Location l = (Location) in[0].get(sc);
-        //    World w = l.getWorld().getWorld();
-        //    EntityVillager v = new EntityVillager(w, in[1].getInt(sc));
-        //    v.setPosition(l.getX(), l.getY(), l.getZ());        
-        //    w.spawnEntity(v);
-        //    return v; 
-        //});
-        
+
         // ---------------------------------------------------------------------  
         // GMap-library   
         // ---------------------------------------------------------------------  
@@ -1907,19 +1835,103 @@ public class MinecraftFunctions
         });
         
         // ---------------------------------------------------------------------  
-        // Effect-library   
+        // particle library   
         // --------------------------------------------------------------------- 
-        sm.registerFunction("effect.playsound", (sc, in) -> 
+        sm.registerFunction("particle.get", (sc, in) -> 
         { 
-            Location l = (Location) in[0].get(sc);
-            Vec3d v = l.getPos();
-            EffectUtils.playSound((ServerWorld) l.getWorld(), Mapper.getSound(in[1].getString(sc)), SoundCategory.MASTER, v.x, v.y, v.z);
+            IParticleData data = Mapper.getParticle(in[0].getString(sc));
+            if(data == ParticleTypes.BLOCK || data == ParticleTypes.FALLING_DUST)
+            {
+                data = new BlockParticleData((ParticleType<BlockParticleData>) data, Mapper.getBlock(in[1].getString(sc)).getDefaultState());
+            }
+            else if(data == ParticleTypes.DUST)
+            {
+                data = new RedstoneParticleData(in[1].getFloat(sc), in[2].getFloat(sc), in[3].getFloat(sc), in[4].getFloat(sc));
+            }
+            else if(data == ParticleTypes.ITEM)
+            {
+                data = new ItemParticleData((ParticleType<ItemParticleData>) data, new ItemStack(Mapper.getItem(in[1].getString(sc))));
+            }
+            return data; 
+        });
+        sm.registerFunction("particle.spawn", (sc, in) -> 
+        { 
+            Location l = ((Location) in[0].get(sc));
+            IParticleData data = ((IParticleData) in[1].get(sc));
+            int count = in.length >= 3 ? in[2].getInt(sc) : 1;
+            double speed = in.length >= 4 ? in[3].getDouble(sc) : 0.0;
+            double offX = in.length >= 5 ? in[4].getDouble(sc) : 0.0;
+            double offY = in.length >= 6 ? in[5].getDouble(sc) : 0.0;
+            double offZ = in.length >= 7 ? in[6].getDouble(sc) : 0.0;
+            ((ServerWorld) l.getWorld()).spawnParticle(data, l.getX(), l.getY(), l.getZ(), count, offX, offY, offZ, speed);
+            return Void.TYPE; 
+        });
+        sm.registerFunction("particle.spawncircle", (sc, in) -> 
+        { 
+            Location l = ((Location) in[0].get(sc));
+            IParticleData data = ((IParticleData) in[1].get(sc));
+            int instances = in[2].getInt(sc);
+            double radius = in[3].getDouble(sc);
+            int count = in.length >= 5 ? in[4].getInt(sc) : 1;
+            double speed = in.length >= 6 ? in[5].getDouble(sc) : 0.0;
+            double offX = in.length >= 7 ? in[6].getDouble(sc) : 0.0;
+            double offY = in.length >= 8 ? in[7].getDouble(sc) : 0.0;
+            double offZ = in.length >= 9 ? in[8].getDouble(sc) : 0.0;
+            double x = l.getX();
+            double y = l.getY();
+            double z = l.getZ();
+            ServerWorld sw = (ServerWorld) l.getWorld();           
+            double angle = 2 * Math.PI / instances;
+            for(int i = 0; i < instances; i++)
+            {
+                sw.spawnParticle(data, x + Math.cos(i * angle) * radius, y, z + Math.sin(i * angle) * radius, count, offX, offY, offZ, speed);
+            }
             return Void.TYPE; 
         });
-        sm.registerFunction("effect.play", (sc, in) -> 
+        sm.registerFunction("particle.spawnline", (sc, in) -> 
         { 
             Location l = ((Location) in[0].get(sc));
-            EffectUtils.spawnParticle((ServerWorld) l.getWorld(), Mapper.getParticle(in[1].getString(sc)), l.getX() + 0.5, l.getY() + 0.5, l.getZ() + 0.5, in[2].getInt(sc));
+            IParticleData data = ((IParticleData) in[1].get(sc));
+            int instances = in[2].getInt(sc);
+            double stepX = in[3].getDouble(sc);
+            double stepY = in[4].getDouble(sc);
+            double stepZ = in[5].getDouble(sc);
+            
+            int count = in.length >= 7 ? in[6].getInt(sc) : 1;
+            double speed = in.length >= 8 ? in[7].getDouble(sc) : 0.0;
+            double offX = in.length >= 9 ? in[8].getDouble(sc) : 0.0;
+            double offY = in.length >= 10 ? in[9].getDouble(sc) : 0.0;
+            double offZ = in.length >= 11 ? in[10].getDouble(sc) : 0.0;
+            double x = l.getX();
+            double y = l.getY();
+            double z = l.getZ();
+            ServerWorld sw = (ServerWorld) l.getWorld();           
+            for(int i = 0; i < instances; i++)
+            {
+                sw.spawnParticle(data, x + i * stepX, y + i * stepY, z + i * stepZ, count, offX, offY, offZ, speed);
+            }
+            return Void.TYPE; 
+        });
+        
+        // ---------------------------------------------------------------------  
+        // particle library   
+        // --------------------------------------------------------------------- 
+        sm.registerFunction("sound.get", (sc, in) -> Mapper.getSound(in[0].getString(sc)));
+        sm.registerFunction("sound.spawn", (sc, in) -> 
+        { 
+            Location l = (Location) in[0].get(sc);
+            ServerWorld sw = (ServerWorld) l.getWorld();
+            float volume = in.length >= 3 ? in[2].getFloat(sc) : 1.0f;
+            float pitch = in.length >= 4 ? in[3].getFloat(sc) : (sw.rand.nextFloat() * 0.1f + 0.9f);
+            sw.playSound(null, l.getX(), l.getY(), l.getZ(), (SoundEvent) in[1].get(sc), SoundCategory.MASTER, volume, pitch);
+            return Void.TYPE; 
+        });
+        sm.registerFunction("sound.spawnforplayer", (sc, in) -> 
+        { 
+            ServerPlayerEntity p = (ServerPlayerEntity) in[0].get(sc);
+            float volume = in.length >= 3 ? in[2].getFloat(sc) : 1.0f;
+            float pitch = in.length >= 4 ? in[3].getFloat(sc) : (p.world.rand.nextFloat() * 0.1f + 0.9f);
+            p.playSound((SoundEvent) in[1].get(sc), volume, pitch);
             return Void.TYPE; 
         });
 
@@ -2019,32 +2031,15 @@ public class MinecraftFunctions
         });
         sm.registerFunction("read.spawnmob", (sc, in) -> 
         {
-            /*Location l = (Location) in[0].get(sc);
-            String data = SnuviUtils.connect(sc, in, 1).replace('\'', '"');
-            try 
-            {
-                CompoundNBT nbt = JsonToNBT.getTagFromJson(data);
-                Vec3d v = l.getPos();
-                Entity ent = AnvilChunkLoader.readWorldEntityPos(nbt, l.getWorld(), v.x, v.y, v.z, true);
-                if(ent == null)
-                {
-                    return null;
-                }
-                else
-                {
-                    ent.setLocationAndAngles(v.x, v.y, v.z, ent.rotationYaw, ent.rotationPitch);
-                    if(ent instanceof EntityLiving)
-                    {
-                        ((EntityLiving) ent).onInitialSpawn(ent.getEntityWorld().getDifficultyForLocation(new BlockPos(ent)), null);
-                    }
-                }  
-                return ent;
-            }
-            catch(NBTException ex)
+            Location l = (Location) in[0].get(sc);
+            ServerWorld sw = (ServerWorld) l.getWorld();
+            CompoundNBT compoundnbt = JsonToNBT.getTagFromJson(SnuviUtils.connect(sc, in, 1)); 
+            Entity entity = EntityType.func_220335_a(compoundnbt, sw, (ent) ->
             {
-                throw new IllegalArgumentException("illegal entity string '" + data + "'");
-            }*/
-            return null;
+                ent.setLocationAndAngles(l.getX(), l.getY(), l.getZ(), ent.rotationYaw, ent.rotationPitch);
+                return sw.summonEntity(ent) ? ent : null;
+            });
+            return entity;
         });
 
         // ---------------------------------------------------------------------  
@@ -2345,4 +2340,95 @@ public class MinecraftFunctions
         }
         return text;
     }
+    
+    private static <T> T launchProjectile(PlayerEntity p, Class<? extends T> projectile, double scale, Object data)
+    {
+        World w = p.world;
+        Entity launch = null;
+
+        if(EntityItemProjectile.class == projectile)
+        {
+            if(data == null)
+            {
+                throw new NullPointerException("Data musn't be null for EntityItemProjectile");
+            }
+            ItemStack stack = (ItemStack) data;
+            if(stack.isEmpty())
+            {
+                throw new IllegalArgumentException("Empty ItemStack not allowed here");
+            }
+            launch = new EntityItemProjectile(p, stack.copy());
+            ((EntityItemProjectile) launch).setHeadingFromThrower(p, p.rotationPitch, p.rotationYaw, 0.0f, 1.5f, 1.0f);
+        }
+        else if(SnowballEntity.class == projectile)
+        {
+            launch = new SnowballEntity(w, p);
+            ((SnowballEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, 0.0f, 1.5f, 1.0f);
+        }
+        else if(EggEntity.class == projectile)
+        {
+            launch = new EggEntity(w, p);
+            ((EggEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, 0.0f, 1.5f, 1.0f);
+        }
+        else if(EnderPearlEntity.class == projectile)
+        {
+            launch = new EnderPearlEntity(w, p);
+            ((EnderPearlEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, 0.0f, 1.5f, 1.0f);
+        }
+        else if(PotionEntity.class == projectile)
+        {
+            launch = new PotionEntity(w, p);
+            ((PotionEntity) launch).setItem((ItemStack) data);
+            ((PotionEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, -20.0f, 0.5f, 1.0f);
+        }
+        else if(ExperienceBottleEntity.class == projectile)
+        {
+            launch = new ExperienceBottleEntity(w, p);
+            ((ExperienceBottleEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, -20.0f, 0.7f, 1.0f);
+        }
+        else if(AbstractArrowEntity.class.isAssignableFrom(projectile))
+        {
+            if(SpectralArrowEntity.class == projectile)
+            {
+                launch = new SpectralArrowEntity(w, p);
+            }
+            else
+            {
+                launch = new ArrowEntity(w, p);
+                if(data != null)
+                {
+                    ((ArrowEntity) launch).setPotionEffect((ItemStack) data);
+                }
+            }
+            ((AbstractArrowEntity) launch).shoot(p, p.rotationPitch, p.rotationYaw, 0.0F, 3.0F, 1.0F);
+        }
+        else if(DamagingProjectileEntity.class.isAssignableFrom(projectile))
+        {
+            Vec3d v = p.getLookVec().scale(10);
+            if(SmallFireballEntity.class == projectile)
+            {
+                launch = new SmallFireballEntity(w, p, v.x, v.y, v.z);
+            }
+            else if(WitherSkullEntity.class == projectile)
+            {
+                launch = new WitherSkullEntity(w, p, v.x, v.y, v.z);
+            }
+            else if(DragonFireballEntity.class == projectile)
+            {
+                launch = new DragonFireballEntity(w, p, v.x, v.y, v.z);
+            }
+            else
+            {
+                launch = new FireballEntity(w, p, v.x, v.y, v.z);
+            }
+        }
+        else
+        {
+            return null;
+        }
+
+        launch.setMotion(launch.getMotion().scale(scale));
+        w.addEntity(launch);
+        return (T) launch;
+    }
 }

+ 25 - 0
src/main/java/me/km/utils/ClientReflectionUtils.java

@@ -2,9 +2,12 @@ package me.km.utils;
 
 import java.lang.reflect.Field;
 import java.util.Map;
+import me.km.overrides.ModPlayerRenderer;
 import static me.km.utils.ReflectionUtils.getField;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.gui.overlay.BossOverlayGui;
+import net.minecraft.client.renderer.entity.EntityRendererManager;
+import net.minecraft.client.renderer.entity.PlayerRenderer;
 import net.minecraftforge.api.distmarker.Dist;
 import net.minecraftforge.api.distmarker.OnlyIn;
 
@@ -21,4 +24,26 @@ public class ClientReflectionUtils
     {
         return !ReflectionUtils.getFieldValue(Map.class, Minecraft.getInstance().ingameGUI.getBossOverlay(), BOSS_BAR_MAP).isEmpty();
     }
+    
+    private final static Field SKIN_MAP = getField(EntityRendererManager.class, "field_178636_l"); // skinMap
+    private final static Field PLAYER_RENDERER = getField(EntityRendererManager.class, "field_178637_m"); // playerRenderer
+    
+    public static void updatePlayerRenderer()
+    {
+        EntityRendererManager erm = Minecraft.getInstance().getRenderManager();
+        ModPlayerRenderer normal = new ModPlayerRenderer(erm, false);
+        ModPlayerRenderer slim = new ModPlayerRenderer(erm, true);
+        
+        ReflectionUtils.setFieldValue(erm, PLAYER_RENDERER, normal);
+        Map<String, PlayerRenderer> map = 
+                ReflectionUtils.getFieldValue(Map.class, erm, SKIN_MAP);
+        
+        org.apache.logging.log4j.LogManager.getLogger().warn("______________________________________________");
+        org.apache.logging.log4j.LogManager.getLogger().warn(map);
+        
+        map.put("default", normal);
+        map.put("slim", slim);
+
+        org.apache.logging.log4j.LogManager.getLogger().warn(map);
+    }
 }

+ 6 - 0
src/main/java/me/km/utils/Mapper.java

@@ -1,5 +1,6 @@
 package me.km.utils;
 
+import net.minecraft.block.Block;
 import net.minecraft.enchantment.Enchantment;
 import net.minecraft.item.Item;
 import net.minecraft.particles.IParticleData;
@@ -34,4 +35,9 @@ public class Mapper
     {
         return ForgeRegistries.ITEMS.getValue(new ResourceLocation(name));
     }
+    
+    public static Block getBlock(String name)
+    {
+        return ForgeRegistries.BLOCKS.getValue(new ResourceLocation(name));
+    }
 }