Hologramme | 1.8 aufwärts kompatibel

Hier könnt ihr anderen Leuten helfen, indem ihr Anleitungen oder praktische Codesegmente zur Verfügung stellt.

Hologramme | 1.8 aufwärts kompatibel

Beitragvon Sep2703 » Di 23. Dez 2014, 20:55

Liebe Postcrafter-Community,

ich habe mir mal die Mühe gemacht und eine Hologramm-Klasse geschrieben.
Diese basiert auf einer Idee von @MaxBukkit.
Ich habe das ganze noch in eine Klasse verpackt und alle Instanzen aus dem NMS- und Craftbukkit-Klassen mithilfe von Reflection instanziiert.
Dadurch, dass ich keine Imports aus den Packages haben, deren Namen sich mit jeder Version ändern, sollte meine Methode in Version 1.8+ funktionieren. Für ältere Versionen wird es jedoch schwierig, da die Klasse net.minecraft.server.EntityArmorStand benötigt wird.
Diese Lösung möchte ich euch nun vorstellen. Ich habe das ganze mit einem Spigot 1.8 Server und einem 1.8 Client lokal auf meinem PC getestet.
Der Code:
Code: Alles auswählen
  1. package de.janhektor.holograms;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.lang.reflect.Method;
  5. import java.util.List;
  6. import org.bukkit.Bukkit;
  7. import org.bukkit.Location;
  8. import org.bukkit.World;
  9. import org.bukkit.entity.Player;
  10. public class HoloAPI {
  11.    
  12.    private List<String> lines;
  13.    private Location loc;
  14.    private static final double ABS = 0.23D;
  15.    private static String path;
  16.    private static String version;
  17.    
  18.    
  19.    static {
  20.       path = Bukkit.getServer().getClass().getPackage().getName();
  21.       version = path.substring(path.lastIndexOf(".")+1, path.length());
  22.    }
  23.    public HoloAPI(Location loc, List<String> lines) {
  24.       this.lines = lines;
  25.       this.loc = loc;
  26.    }
  27.    
  28.    // Boolean successfully
  29.    public boolean display(Player p) {
  30.       Location displayLoc = loc.clone().add(0, (ABS * lines.size()) - 1.97D, 0);
  31.       for (int i = 0; i < lines.size(); i++) {
  32.          Object packet = this.getPacket(this.loc.getWorld(), displayLoc.getX(), displayLoc.getY(), displayLoc.getZ(), this.lines.get(i));
  33.          if (packet == null) return false;
  34.          this.sendPacket(p, packet);
  35.          displayLoc.add(0, -ABS, 0);
  36.       }
  37.       
  38.       return true;
  39.    }
  40.    
  41.    public Object getPacket(World w, double x, double y, double z, String text) {
  42.       try {
  43.          Class<?> armorStand = Class.forName("net.minecraft.server." + version + ".EntityArmorStand");
  44.          Class<?> worldClass = Class.forName("net.minecraft.server." + version + ".World");
  45.          Class<?> nmsEntity = Class.forName("net.minecraft.server." + version + ".Entity");
  46.          Class<?> craftWorld = Class.forName("org.bukkit.craftbukkit." + version + ".CraftWorld");
  47.          Class<?> packetClass = Class.forName("net.minecraft.server." + version + ".PacketPlayOutSpawnEntityLiving");
  48.          Class<?> entityLivingClass = Class.forName("net.minecraft.server." + version + ".EntityLiving");
  49.          Constructor<?> cww = armorStand.getConstructor(new Class<?>[] { worldClass });
  50.          Object craftWorldObj = craftWorld.cast(w);
  51.          Method getHandleMethod = craftWorldObj.getClass().getMethod("getHandle", new Class<?>[0]);
  52.          Object entityObject = cww.newInstance(new Object[] { getHandleMethod.invoke(craftWorldObj, new Object[0]) });
  53.          Method setCustomName = entityObject.getClass().getMethod("setCustomName", new Class<?>[] { String.class });
  54.          setCustomName.invoke(entityObject, new Object[] { text });
  55.          Method setCustomNameVisible = nmsEntity.getMethod("setCustomNameVisible", new Class[] { boolean.class });
  56.          setCustomNameVisible.invoke(entityObject, new Object[] { true });
  57.          Method setGravity = entityObject.getClass().getMethod("setGravity", new Class<?>[] { boolean.class });
  58.          setGravity.invoke(entityObject, new Object[] { false });
  59.          Method setLocation = entityObject.getClass().getMethod("setLocation", new Class<?>[] { double.class, double.class, double.class, float.class, float.class });
  60.          setLocation.invoke(entityObject, new Object[] { x, y, z, 0.0F, 0.0F });
  61.          Method setInvisible = entityObject.getClass().getMethod("setInvisible", new Class<?>[] { boolean.class });
  62.          setInvisible.invoke(entityObject, new Object[] { true });
  63.          Constructor<?> cw = packetClass.getConstructor(new Class<?>[] { entityLivingClass });
  64.          Object packetObject = cw.newInstance(new Object[] { entityObject });
  65.          return packetObject;
  66.       } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
  67.          e.printStackTrace();
  68.       }
  69.       return null;
  70.    }
  71.    
  72.     private void sendPacket(Player p, Object packet) {
  73.         String path = Bukkit.getServer().getClass().getPackage().getName();
  74.         String version = path.substring(path.lastIndexOf(".") + 1, path.length());
  75.         try {
  76.            Method getHandle = p.getClass().getMethod("getHandle");
  77.            Object entityPlayer = getHandle.invoke(p);
  78.            Object pConnection = entityPlayer.getClass().getField("playerConnection").get(entityPlayer);
  79.            Class<?> packetClass = Class.forName("net.minecraft.server." + version + ".Packet");
  80.            Method sendMethod = pConnection.getClass().getMethod("sendPacket", new Class[] { packetClass });
  81.            sendMethod.invoke(pConnection, new Object[] { packet });
  82.         } catch (Exception e) {
  83.            e.printStackTrace();
  84.         }
  85.      }
  86. }



Eine Mini-Anleitung zur Verwendung:
1. Erstelle dir eine Instanz von "HoloAPI" und übergeben an den Konstruktor: Location (Der Ort, an dem die unterste Zeile des Holograms erscheint); List<String> (Alle Zeilen, das erste Elemente entspricht später der obersten Zeile)
2. Rufe die Methode display(Player) auf und über einen Player (Bukkit-Interface). Nur dieser Spieler wird das Hologramm sehen. Du kannst aber natürlich die Spieler-Liste auf dem Server iterieren und dann Hologramm an jeden Spieler senden.



Meine TODO-Liste:
  • Methode zum Entfernen des Hologramms einbauen
  • Methode zum Updaten des Hologramms einbauen
  • Methoden zum Ändern des Textes (Text ändern + Impliziter Aufruf der Update-Methode) einbauen


So, das war's auch schon wieder.
Wenn ihr Bugs findet oder eine Idee zur Verbesserung habt, könnt ihr natürlich gerne antworten.
Viel Spaß damit!


LG
Janhektor / Sep2703
Du möchtest programmieren lernen oder dein Bukkit-/Spigot-Wissen erweitern?
Hier habe ich für dich kostenlose Tutorials: https://youtube.com/janhektor
Benutzeravatar
Sep2703
 
Beiträge: 677
Registriert: Mi 8. Jan 2014, 15:13
Wohnort: 127.0.0.1

Re: Hologramme | 1.8 aufwärts kompatibel

Beitragvon Mr9xDumm » Di 23. Dez 2014, 22:10

Juhu danke <3
Mag Apple
Benutzeravatar
Mr9xDumm
 
Beiträge: 104
Registriert: So 2. Nov 2014, 19:22

Re: Hologramme | 1.8 aufwärts kompatibel

Beitragvon ilouHD » Di 23. Dez 2014, 22:52

Ich hätte es nicht besser machen können. Habe zwar eine funktionierende Klasse, aber die ist im Leben nicht so gut wie deine. Werde sie mal wechseln ;D
Bild
Benutzeravatar
ilouHD
 
Beiträge: 1733
Registriert: Do 9. Jan 2014, 14:49

Re: Hologramme | 1.8 aufwärts kompatibel

Beitragvon Jofkos » Di 23. Dez 2014, 23:15

ich würd Klassen, Konstruktoren und Methoden als static speichern, damit du sie nicht immer wieder instanzieren musst.
Jofkos

...........

..Bild
Benutzeravatar
Jofkos
 
Beiträge: 1537
Registriert: So 16. Jun 2013, 22:45

Re: Hologramme | 1.8 aufwärts kompatibel

Beitragvon Sep2703 » Mi 24. Dez 2014, 09:23

So, ich habe ich verbesserte Lösung mit Cache entwickelt.
Nun werden Klassen schon im statischen Block einmalig initialisiert.
Bei Mehrfachnutzung (z.B. wenn man Texte neu setzt oder "Animationen" damit kreiert) sollte das einen deutlichen Geschwindigkeitsschub geben.
Der Code ebenfalls kostenlos und frei zur Verfügung gestellt:
Code: Alles auswählen
  1. package de.janhektor.holo;
  2. import java.lang.reflect.Constructor;
  3. import java.lang.reflect.InvocationTargetException;
  4. import java.lang.reflect.Method;
  5. import java.util.List;
  6. import org.bukkit.Bukkit;
  7. import org.bukkit.Location;
  8. import org.bukkit.World;
  9. import org.bukkit.entity.Player;
  10. public class HoloAPI {
  11.    
  12.    private List<String> lines;
  13.    private Location loc;
  14.    
  15.    private static final double ABS = 0.23D;
  16.    private static String path;
  17.    private static String version;
  18.    
  19.    /*
  20.     * Cache for getPacket()-Method
  21.     */
  22.    private static Class<?> armorStand;
  23.    private static Class<?> worldClass;
  24.    private static Class<?> nmsEntity;
  25.    private static Class<?> craftWorld;
  26.    private static Class<?> packetClass;
  27.    private static Class<?> entityLivingClass;
  28.    private static Constructor<?> armorStandConstructor;
  29.    
  30.    /*
  31.     * Cache for sendPacket()-Method
  32.     */
  33.    private static Class<?> nmsPacket;
  34.    
  35.    
  36.    static {
  37.       path = Bukkit.getServer().getClass().getPackage().getName();
  38.       version = path.substring(path.lastIndexOf(".")+1, path.length());
  39.       
  40.       try {
  41.          armorStand = Class.forName("net.minecraft.server." + version + ".EntityArmorStand");
  42.          worldClass = Class.forName("net.minecraft.server." + version + ".World");
  43.          nmsEntity = Class.forName("net.minecraft.server." + version + ".Entity");
  44.          craftWorld = Class.forName("org.bukkit.craftbukkit." + version + ".CraftWorld");
  45.          packetClass = Class.forName("net.minecraft.server." + version + ".PacketPlayOutSpawnEntityLiving");
  46.          entityLivingClass = Class.forName("net.minecraft.server." + version + ".EntityLiving");
  47.          armorStandConstructor = armorStand.getConstructor(new Class[] { worldClass });
  48.          
  49.          nmsPacket = Class.forName("net.minecraft.server." + version + ".Packet");
  50.       } catch (ClassNotFoundException | NoSuchMethodException | SecurityException ex) {
  51.          System.err.println("Error - Classes not initialized!");
  52.          ex.printStackTrace();
  53.       }
  54.    }
  55.    public HoloAPI(Location loc, List<String> lines) {
  56.       this.lines = lines;
  57.       this.loc = loc;
  58.    }
  59.    
  60.    // Boolean successfully
  61.    public boolean display(Player p) {
  62.       Location displayLoc = loc.clone().add(0, (ABS * lines.size()) - 1.97D, 0);
  63.       for (int i = 0; i < lines.size(); i++) {
  64.          Object packet = this.getPacket(this.loc.getWorld(), displayLoc.getX(), displayLoc.getY(), displayLoc.getZ(), this.lines.get(i));
  65.          if (packet == null) return false;
  66.          this.sendPacket(p, packet);
  67.          displayLoc.add(0, -ABS, 0);
  68.       }
  69.       
  70.       return true;
  71.    }
  72.    
  73.    private Object getPacket(World w, double x, double y, double z, String text) {
  74.       try {
  75.          Object craftWorldObj = craftWorld.cast(w);
  76.          Method getHandleMethod = craftWorldObj.getClass().getMethod("getHandle", new Class<?>[0]);
  77.          Object entityObject = armorStandConstructor.newInstance(new Object[] { getHandleMethod.invoke(craftWorldObj, new Object[0]) });
  78.          Method setCustomName = entityObject.getClass().getMethod("setCustomName", new Class<?>[] { String.class });
  79.          setCustomName.invoke(entityObject, new Object[] { text });
  80.          Method setCustomNameVisible = nmsEntity.getMethod("setCustomNameVisible", new Class[] { boolean.class });
  81.          setCustomNameVisible.invoke(entityObject, new Object[] { true });
  82.          Method setGravity = entityObject.getClass().getMethod("setGravity", new Class<?>[] { boolean.class });
  83.          setGravity.invoke(entityObject, new Object[] { false });
  84.          Method setLocation = entityObject.getClass().getMethod("setLocation", new Class<?>[] { double.class, double.class, double.class, float.class, float.class });
  85.          setLocation.invoke(entityObject, new Object[] { x, y, z, 0.0F, 0.0F });
  86.          Method setInvisible = entityObject.getClass().getMethod("setInvisible", new Class<?>[] { boolean.class });
  87.          setInvisible.invoke(entityObject, new Object[] { true });
  88.          Constructor<?> cw = packetClass.getConstructor(new Class<?>[] { entityLivingClass });
  89.          Object packetObject = cw.newInstance(new Object[] { entityObject });
  90.          return packetObject;
  91.       } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
  92.          e.printStackTrace();
  93.       }
  94.       return null;
  95.    }
  96.    
  97.     private void sendPacket(Player p, Object packet) {
  98.         try {
  99.            Method getHandle = p.getClass().getMethod("getHandle");
  100.            Object entityPlayer = getHandle.invoke(p);
  101.            Object pConnection = entityPlayer.getClass().getField("playerConnection").get(entityPlayer);
  102.            Method sendMethod = pConnection.getClass().getMethod("sendPacket", new Class[] { nmsPacket });
  103.            sendMethod.invoke(pConnection, new Object[] { packet });
  104.         } catch (Exception e) {
  105.            e.printStackTrace();
  106.         }
  107.      }
  108. }
Du möchtest programmieren lernen oder dein Bukkit-/Spigot-Wissen erweitern?
Hier habe ich für dich kostenlose Tutorials: https://youtube.com/janhektor
Benutzeravatar
Sep2703
 
Beiträge: 677
Registriert: Mi 8. Jan 2014, 15:13
Wohnort: 127.0.0.1

Re: Hologramme | 1.8 aufwärts kompatibel

Beitragvon zwoerni » Mo 5. Jan 2015, 00:46

Ich habe meinen eigenen Methoden, das gleiche Prinzip.
Jedoch gibt es folgendes Problem: Sobald ich mich ~200 Blöcke vom Hologramm entferne wird es gelöscht.
Habt ihr dieses Problem auch?
Benutzeravatar
zwoerni
 
Beiträge: 25
Registriert: Mo 15. Sep 2014, 15:20
Wohnort: Halle (Saale) - Sachsen Anhalt

Re: Hologramme | 1.8 aufwärts kompatibel

Beitragvon ilouHD » Mo 5. Jan 2015, 01:06

Ich hab das Problem, dass meine trotz despawnpaket nicht weg gehen :(

Irgendwas hab ich falsch gemacht...
Bild
Benutzeravatar
ilouHD
 
Beiträge: 1733
Registriert: Do 9. Jan 2014, 14:49


Re: Hologramme | 1.8 aufwärts kompatibel

Beitragvon zwoerni » Mo 5. Jan 2015, 10:30

Ich habe gerade mal deine Methoden ausprobiert, dasselbe Problem - die Hologramme "despawnen", wenn man sich ~200 Blöcke entfernt.
Benutzeravatar
zwoerni
 
Beiträge: 25
Registriert: Mo 15. Sep 2014, 15:20
Wohnort: Halle (Saale) - Sachsen Anhalt

Re: Hologramme | 1.8 aufwärts kompatibel

Beitragvon ilouHD » Mo 5. Jan 2015, 10:37

Wieso macht man es eigentlich nicht mit WitherSkulls. Bei einer Direction von null, sollte er unsichtbar sein.
Bild
Benutzeravatar
ilouHD
 
Beiträge: 1733
Registriert: Do 9. Jan 2014, 14:49

Nächste

Zurück zu Anleitungen

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 3 Gäste