Hologramme - 1.8+

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

Hologramme - 1.8+

Beitragvon Aquaatic » Sa 4. Apr 2015, 11:09

So ich hab mir mal überlegt, dass ich hier auch mal ein Tutorial machen will :D
Ich weiß, es gibt hier son 300.000 Tutorials dazu, aber keine Versionsunabhängige und entfernbare (@Janhektor :D)

Ich habe versucht das ganze Objektorientiert zu gestalten und so einfach wie möglich zu gestalten :) Verbesserungsvorschläge sind erwünscht!
btw. es wird nicht einfach (Reflection-Kentnisse vorrausgesetzt)

Dieses Tutorial benötigt folgende Reflectionklasse: http://pastebin.com/fsqneE5b

Los gehts!

Zunächst erstellen wir uns eine Klasse mit dem Namen 'Hologram' und speichern darin mehrere Variablen:
Code: Alles auswählen
  1. private List<String> text;
  2.    private List<Player> players;
  3.    private List<Object> stands;
  4.    private Location loc;
  5.    private double height;
  6.    
  7.    /**
  8.     * @param text <p>Der anzuzeigende Text</p>
  9.     * @param loc <p>Die Position, an der das Hologramm gespawnt werden soll</p>
  10.     * @param pl <p>Eine Instanz einer Hauptklasse, um das Hologramm zu registrieren.</p>
  11.     */
  12.    public Hologram(String[] text, Location loc, Plugin pl) {
  13.       this.text = new ArrayList<>(Arrays.asList(text));
  14.       this.players = new ArrayList<Player>();
  15.       this.stands = new ArrayList<Object>();
  16.       this.loc = loc;
  17.       Manager.register(this, pl);
  18.    }
  19.    /**
  20.     * @param loc <p>Die Position, an der das Hologramm gespawnt werden soll</p>
  21.     * @param pl <p>Eine Instanz einer Hauptklasse, um das Hologramm zu registrieren.</p>
  22.     */
  23.    public Hologram(Location loc, Plugin pl) {
  24.       this(new String[] {}, loc, pl);
  25.    }


Variablen:
- text: Der Text (1 Eintrag = 1 Zeile)
- stands: Instanzen der NMS Klass EnitityArmorStand
- loc: Die Position des Hologramms
- height: Wird zur weiteren 'Berechnung' ebnötigt

Die Konstruktoren initialisieren die Variablen natürlich erstmal :D
Nun habe ich ein paar Methoden erstellt, um den Text zu bearbeiten oder Spieler hinzuzufügen, denen das Hologramm angezeigt werden soll.
Code: Alles auswählen
  1. /**
  2.     * <p>Fügt dem Text eine Linie hinzu</p>
  3.     * <p>Danach muss die setup Methode erneut aufgerufen werden, um die Einstellungen zu übernehmen<p>
  4.     * @param text <p>Der Text, der hinzugefügt werden soll</p>
  5.     */
  6.    public void addLine(String text) {
  7.       this.text.add(text);
  8.    }
  9.    /**
  10.     * <p>Weist der Zeile den Text zu.</p>
  11.     * @param index <p>Zeile (beginnt bei 0)</p>
  12.     * @param text <p>Der zu setzende Text</p>
  13.     */
  14.    public void setLine(int index, String text) {
  15.       this.text.set(index, text);
  16.    }
  17.    /**
  18.     * <p>Löscht eine Zeile</p>
  19.     * @param index <p>Zu löschende Zeile (ab 0)</p>
  20.     */
  21.    public void removeLine(int index) {
  22.       this.text.remove(index);
  23.    }
  24.    /**
  25.     * <p>Löscht eine Zeile mit dem angegebenen Text</p>
  26.     * @param text <p>Der Text, welcher gelöscht werden soll.</p>
  27.     */
  28.    public void removeLine(String text) {
  29.       this.text.remove(text);
  30.    }
  31.    /**
  32.     * <p>Fügt einen Spieler hinzu, dem das Hologramm angezeigt werden soll</p>
  33.     * @param p <p>Der hinzuzufügende Spieler</p>
  34.     */
  35.    public void addPlayer(Player p) {
  36.       players.add(p);
  37.    }
  38.    /**
  39.     * <p>Löscht einen zuvor hinzugefügten Spieler.</p>
  40.     * @param p <p>Der zu löschende Spieler</p>
  41.     */
  42.    public void removePlayer(Player p) {
  43.       players.remove(p);
  44.    }

Sollte sich selbst erklären - wenn nicht bitte wegklicken :)

Nun fügen wir Instanzen der EntityArmorStand Klasse zu unserer Liste hinzu. Dazu benötigen wir erstmal ein paar Klassen, Methoden und Konstruktoren als Konstanten (ein paar benötigen wir erst später).
Code: Alles auswählen
  1. private static final Class<?> CLASS_SPAWN_ENTITY_LIVING = Reflection.getNMSClass("PacketPlayOutSpawnEntityLiving");
  2.    private static final Class<?> CLASS_ARMOR_STAND = Reflection.getNMSClass("EntityArmorStand");
  3.    private static final Class<?> CLASS_ENTITY_DESTROY = Reflection.getNMSClass("PacketPlayOutEntityDestroy");
  4.    private static final Class<?> CLASS_PACKET = Reflection.getNMSClass("Packet");
  5.    private static final Class<?> CLASS_WORLD = Reflection.getNMSClass("World");
  6.    private static final Class<?> CLASS_ENTITY_LIVING = Reflection.getNMSClass("EntityLiving");
  7.    
  8.    private static final Constructor<?> CONSTRUCTOR_ENTITY_DESTROY = Reflection.getConstructor(CLASS_ENTITY_DESTROY);
  9.    private static final Constructor<?> CONSTRUCTOR_SPAWN_ENTITY_LIVING = Reflection.getConstructor(CLASS_SPAWN_ENTITY_LIVING, CLASS_ENTITY_LIVING);
  10.    private static final Constructor<?> CONSTRUCTOR_ARMOR_STAND = Reflection.getConstructor(CLASS_ARMOR_STAND, CLASS_WORLD);


Nun erstellen wir uns eine Methode, um die ArmorStands zu "erstellen". Diese nennen wir setup(). Sie wird folgendes für uns erledigen:
- Hologramme zunächst von allen Spielern entfernen
- Neue Instanzen erstellen für jede Zeile
- Hologramme zu den Spielern senden

Die Methoden zum senden und entfernen fügen wir später hinzu.

Also:
Code: Alles auswählen
  1. public void setup() throws Exception {
  2.       removeFromPlayers();
  3.       stands.clear();
  4.       height = loc.getY()-1.45;
  5.       if(text.size() != 1) {
  6.          height += text.size()*0.3;
  7.       }
  8.       //Instanzen erstellen
  9.       sendToPlayers();
  10.    }

Erklärung:
Zunächst setzten wir die Liste 'stands' zurück, sonst können manche Texte doppelt vorkommen und das wollen wir ja nicht, oder doch? :lol:
Danach substrahieren von der Y-Koordinate der Location 1.45, was sich für mich als richtig angesehen hat (ich glaube, dass ein 'Baby-ArmorStand' 1.45 Blöcke hoch ist). Und nun füge ich für jede Textzeile 0.3 hinzu, da das der Abstand sein wird, den die Texte in der Y-Achse haben werden. Dann erstelle ich die Instanzen (kommt gleich! :D) und sende das Hologramm zu den Spielern.

Nun zur Instanzenerstellung:
Code: Alles auswählen
  1. World w = loc.getWorld();
  2.       Object world = w.getClass().getDeclaredMethod("getHandle").invoke(w);
  3.       for(String text : this.text) {
  4.          try {
  5.             Object o = CONSTRUCTOR_ARMOR_STAND.newInstance(world);
  6.             CLASS_ARMOR_STAND.getMethod("setSmall", boolean.class).invoke(o, true);
  7.             CLASS_ARMOR_STAND.getMethod("setLocation", double.class, double.class, double.class, float.class, float.class).invoke(o, loc.getX(), height, loc.getZ(), loc.getYaw(), loc.getPitch());
  8.             CLASS_ARMOR_STAND.getMethod("setCustomName", String.class).invoke(o, text);
  9.             CLASS_ARMOR_STAND.getMethod("setGravity", boolean.class).invoke(o, true);
  10.             CLASS_ARMOR_STAND.getMethod("setInvisible", boolean.class).invoke(o, true);
  11.             CLASS_ARMOR_STAND.getMethod("setCustomNameVisible", boolean.class).invoke(o, true);
  12.             
  13.             height -= 0.3;
  14.             stands.add(o);
  15.          } catch(Exception e) {
  16.             throw new Exception("Error at setting up the Hologram." + e.getMessage());
  17.          }
  18.       }

Erklärung:
Erstmal gette ich die Welt der Location und rufe die Methode "getHandle" auf, damit ich das world-Objekt später zur Erstellung der ArmorStands verwenden kann. Dann iteriere ich durch alle Zeilen und erstelle eine neue Instanz der Armor-Stand Klasse. Danach mache ich ihn klein, setze die Location, den CustomName, die Schwerkraft (is aber eigendlich egal :D), Unsichtbarkeit, den CustomName sichtbar und substrahiere 0.3 (wie vorhin schon erwähnt von seiner Höhe. Da das ganze Reflection Zeug gerne mal Exceptions schmeißt fange ich alle ab.

Nun bringt uns das ganze überhaupt nichts, solange wir es nicht den Spielern senden können! Also:
Code: Alles auswählen
  1. /**
  2.     * <p>Sendet das Hologramm den Spielern (nicht notwendig - wird von der Methode setup intern aufgerufen)<p>
  3.     */
  4.    public void sendToPlayers() throws Exception {
  5.       for(Player p : players) {
  6.          Object nmsPlayer = Reflection.getNMSPlayer(p);
  7.          Object playerConnection = getPlayerConnection(nmsPlayer).get(nmsPlayer);
  8.          Method sendPacket = playerConnection.getClass().getDeclaredMethod("sendPacket", CLASS_PACKET);
  9.          for(Object armor : stands) {
  10.             Object packet = CONSTRUCTOR_SPAWN_ENTITY_LIVING.newInstance(armor);
  11.             sendPacket.invoke(playerConnection, packet);
  12.          }
  13.       }
  14.    }

Erklärung: Ich hole mir bei jedem Spieler die NMS-Player Instanz, gette die playerConnection. Danach iteriere ich durch alle EnityArmorStands und erstelle eine neue Instanz der PacketPlayOutSpawnEntityLiving-Klasse mittels dieser. Danach sende ich noch das Packet über die sendPacket Methode.
Jetzt sind wir ja schon fast fertig, bis auf, dass man das Hologramm nicht entfernen kann.
Dazu machen wir fast dasselbe, bis auf, dass wir ein anderes Packet verwenden.
Code: Alles auswählen
  1. /**
  2.     * <p>Zerstört das Hologramm den Spielern (nicht notwendig - wird intern von den Methoden setup und unregister aufgerufen)<p>
  3.     * @throws Exception
  4.     */
  5.    public void removeFromPlayers() throws Exception {
  6.       for(Player p : players) {
  7.          Object nmsPlayer = Reflection.getNMSPlayer(p);
  8.          Object playerConnection = getPlayerConnection(nmsPlayer).get(nmsPlayer);
  9.          Method sendPacket = playerConnection.getClass().getDeclaredMethod("sendPacket", CLASS_PACKET);
  10.          Method entityID = CLASS_ARMOR_STAND.getMethod("getId");
  11.          for(Object armor : stands) {
  12.             Object packet = CONSTRUCTOR_ENTITY_DESTROY.newInstance();
  13.             int id = (Integer) entityID.invoke(armor);
  14.             int[] arr = new int[] { id };
  15.             Field f = packet.getClass().getDeclaredField("a");
  16.             f.setAccessible(true);
  17.             f.set(packet, arr);
  18.             sendPacket.invoke(playerConnection, packet);
  19.          }
  20.       }
  21.    }

Es bleibt alles fast genau gleich bis zur for-Schleife. Davor hole ich mir noch die Methode getId, welche ein int mit dem Wert der Entity-ID zurückgibt. Nun iteriere ich wieder durch alle ArmorStands. Dann hole ich mir von jedem die ID. Nun hat der Constructor bei mir (warum auch immer) nicht funktioniert. Deshalb habe ich in den Sourcecode geschaut, und gesehen, dass das Field a ein int[] ist. Dieses wird im Constructor mit den vermeintlichen EnitityIDs belegt. Daher greife ich auf dieses Field zu und setze es auf ein zuvor erstelltes Integer-Array (als Inhalt die EntityID). Zuletzt sende ich dem Spieler noch das Packet.

Nun... wie in den JavaDocs der removeFromPlayers-Methode steht gibt es noch eine unregister-Methode. Diese setzt alle Fields auf null und entfernt die Hologramme. Das sieht dann wie folgt aus:
Code: Alles auswählen
  1. /**
  2.     * <b>Alle Werte im Hologramm werden gelöscht und das Hologramm den Spielern nicht mehr angezeigt <b>(ruft intern removeFromPlayers auf)
  3.     * @throws Exception
  4.     */
  5.    public void unregister() throws Exception {
  6.       removeFromPlayers();
  7.       this.text = null;
  8.       this.players = null;
  9.       this.stands = null;
  10.       this.loc = null;
  11.       this.height = 0;
  12.       this.screens = null;
  13.       Manager.unregister(this);
  14.    }


Das war es dann! Zur Verwendung einfach eine neue Instanz der Hologram-Klasse erstellen, den Text bearbeiten, und die setup Methode aufrufen!

Viel Spaß!

Achtung: Könnte Spuren von TouchScreens enthalten, das war ursprünglich auch noch drinnen :D
Mit freundlichen Grüßen
~ Aquaatic
Benutzeravatar
Aquaatic
 
Beiträge: 148
Registriert: Mo 16. Feb 2015, 12:51

Re: Hologramme - 1.8+

Beitragvon ilouHD » Sa 4. Apr 2015, 11:40

Coole Klasse - wo wie die von Janhektor/Sep2703 auch.
Am besten wäre es jetzt noch, wenn du noch das "laufen" zu einer Location rein machen könntest (Vektor zu einer Location)

Das wäre echt nett von dir.

Dann kann ich diese Klasse auch in mein Plugin einbauen.
Bild
Benutzeravatar
ilouHD
 
Beiträge: 1733
Registriert: Do 9. Jan 2014, 14:49

Re: Hologramme - 1.8+

Beitragvon Aquaatic » Sa 4. Apr 2015, 12:49

Laufende Hologramme? o: Kann ich mal schaun, ob ich das hinbekomm :D
Mit freundlichen Grüßen
~ Aquaatic
Benutzeravatar
Aquaatic
 
Beiträge: 148
Registriert: Mo 16. Feb 2015, 12:51

Re: Hologramme - 1.8+

Beitragvon ilouHD » Sa 4. Apr 2015, 16:09

Aquaatic hat geschrieben:Laufende Hologramme? o: Kann ich mal schaun, ob ich das hinbekomm :D


Also, dass die nicht zu einer Location teleportiert werden, sondern einen Vektor da hin bekommen und an der Location dann stoppen.
Bild
Benutzeravatar
ilouHD
 
Beiträge: 1733
Registriert: Do 9. Jan 2014, 14:49

Re: Hologramme - 1.8+

Beitragvon Aquaatic » Sa 4. Apr 2015, 16:46

jaja, das ist mir schon klar :D
Mit freundlichen Grüßen
~ Aquaatic
Benutzeravatar
Aquaatic
 
Beiträge: 148
Registriert: Mo 16. Feb 2015, 12:51

Re: Hologramme - 1.8+

Beitragvon DerFreys » Sa 2. Mai 2015, 12:08

Bevor er sowas machen sollte könnte er mir mal die getPlayerConnection(Method) Methode senden :D
Die finde ich nähmlich nirgends in dem Tutorial und in der Reflection Klasse ist diese leider auch nicht :/
Wäre geil wenn du sie schicken könntest
Benutzeravatar
DerFreys
 
Beiträge: 26
Registriert: Sa 28. Feb 2015, 00:28

Re: Hologramme - 1.8+

Beitragvon ilouHD » Sa 2. Mai 2015, 18:00

GTRobin hat geschrieben:Bevor er sowas machen sollte könnte er mir mal die getPlayerConnection(Method) Methode senden :D
Die finde ich nähmlich nirgends in dem Tutorial und in der Reflection Klasse ist diese leider auch nicht :/
Wäre geil wenn du sie schicken könntest

Ich schätze mal, dass die Klasse nur die playerConnection returnt:
Tipp zum selber machen:
Code: Alles auswählen
  1. ((CraftPlayer)player).getHandle().playerConnection
Bild
Benutzeravatar
ilouHD
 
Beiträge: 1733
Registriert: Do 9. Jan 2014, 14:49

Re: Hologramme - 1.8+

Beitragvon D3SOX » Mo 11. Mai 2015, 16:18

Aquaatic hat geschrieben:Laufende Hologramme? o: Kann ich mal schaun, ob ich das hinbekomm :D

Sowas würde ich mir auch wünschen. Sitze schon den ganzen Tag daran, aber bekomme es nicht hin :(
Benutzeravatar
D3SOX
 
Beiträge: 17
Registriert: Fr 3. Apr 2015, 14:08

Re: Hologramme - 1.8+

Beitragvon Piet » Mo 15. Jun 2015, 09:40

Hey,

kennt jemand eine Methode, um ein Item ganz oben auf den Armorstand zu setzen? Normalerweise ist das ja setHelmet, hier weiß ich aber nicht wie das umsetzbar ist.


Danke im Voraus!

EDIT: Habe es mal so probiert, das klappt aber auch nicht:
Code: Alles auswählen
  1. CLASS_ARMOR_STAND.getMethod("setHelmet", ItemStack.class).invoke(o, new ItemStack(Item.getById(1)));


Code: Alles auswählen
  1. java.lang.NoSuchMethodException: net.minecraft.server.v1_8_R3.EntityArmorStand.setHelmet(net.minecraft.server.v1_8_R3.ItemStack)


Geht das eventuell über die setEquipment Methode?
Benutzeravatar
Piet
 
Beiträge: 114
Registriert: Do 30. Okt 2014, 15:16

Re: Hologramme - 1.8+

Beitragvon Aquaatic » Mo 15. Jun 2015, 13:15

Ja, setEquipment - für die Slots schau mal hier: http://wiki.vg/Protocol#Entity_Equipment ;)
Mit freundlichen Grüßen
~ Aquaatic
Benutzeravatar
Aquaatic
 
Beiträge: 148
Registriert: Mo 16. Feb 2015, 12:51

Nächste

Zurück zu Anleitungen

Wer ist online?

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