diff --git a/src/de/steamwar/lobby/Config.java b/src/de/steamwar/lobby/Config.java
index e5c055c..15ad255 100644
--- a/src/de/steamwar/lobby/Config.java
+++ b/src/de/steamwar/lobby/Config.java
@@ -19,23 +19,30 @@
package de.steamwar.lobby;
+import de.steamwar.lobby.display.Hologram;
import de.steamwar.lobby.portal.Portal;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
public class Config {
+ static {
+ ConfigurationSerialization.registerClass(Portal.class);
+ ConfigurationSerialization.registerClass(Hologram.class);
+ }
+
private final FileConfiguration yml;
public Config(FileConfiguration yml) {
this.yml = yml;
- ConfigurationSerialization.registerClass(Portal.class);
yml.getList("portals", Portal.getPortals());
+ yml.getList("holograms", Hologram.getHolograms());
}
public void save() {
yml.set("portals", Portal.getPortals());
+ yml.set("holograms", Hologram.getHolograms());
LobbySystem.getPlugin().saveConfig();
}
diff --git a/src/de/steamwar/lobby/command/PortalCommand.java b/src/de/steamwar/lobby/command/PortalCommand.java
index 726e19f..053a601 100644
--- a/src/de/steamwar/lobby/command/PortalCommand.java
+++ b/src/de/steamwar/lobby/command/PortalCommand.java
@@ -3,7 +3,6 @@ package de.steamwar.lobby.command;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
-import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.RegionSelector;
import de.steamwar.command.SWCommand;
@@ -12,7 +11,6 @@ import de.steamwar.lobby.LobbySystem;
import de.steamwar.lobby.portal.*;
import de.steamwar.sql.SteamwarUser;
import lombok.Data;
-import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
@@ -23,8 +21,6 @@ import java.util.List;
public class PortalCommand extends SWCommand {
- private static final WorldEditPlugin worldEditPlugin = (WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit");
-
public PortalCommand() {
super("portal");
}
diff --git a/src/de/steamwar/lobby/Displayable.java b/src/de/steamwar/lobby/display/Displayable.java
similarity index 98%
rename from src/de/steamwar/lobby/Displayable.java
rename to src/de/steamwar/lobby/display/Displayable.java
index f8836a2..52ab923 100644
--- a/src/de/steamwar/lobby/Displayable.java
+++ b/src/de/steamwar/lobby/display/Displayable.java
@@ -17,7 +17,7 @@
* along with this program. If not, see .
*/
-package de.steamwar.lobby;
+package de.steamwar.lobby.display;
import de.steamwar.lobby.listener.BasicListener;
import org.bukkit.Bukkit;
diff --git a/src/de/steamwar/lobby/display/Hologram.java b/src/de/steamwar/lobby/display/Hologram.java
new file mode 100644
index 0000000..7a2e68a
--- /dev/null
+++ b/src/de/steamwar/lobby/display/Hologram.java
@@ -0,0 +1,156 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2021 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.lobby.display;
+
+import com.comphenix.tinyprotocol.Reflection;
+import de.steamwar.lobby.listener.Protocol;
+import org.bukkit.Location;
+import org.bukkit.configuration.serialization.ConfigurationSerializable;
+import org.bukkit.entity.Player;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.util.*;
+
+public class Hologram implements ConfigurationSerializable {
+
+ private static final Class> dataWatcherObject = Reflection.getClass("{nms}.DataWatcherObject");
+ private static final Class> dataWatcherRegistry = Reflection.getClass("{nms}.DataWatcherRegistry");
+ private static final Class> dataWatcherSerializer = Reflection.getClass("{nms}.DataWatcherSerializer");
+ private static final Reflection.ConstructorInvoker dataWatcherObjectConstructor = Reflection.getConstructor(dataWatcherObject, int.class, dataWatcherSerializer);
+ private static Object getDataWatcherObject(int index, Class> type) {
+ for(Field field : dataWatcherRegistry.getFields()) {
+ if(dataWatcherSerializer.isAssignableFrom(field.getType()) && type.equals(((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0])) {
+ try {
+ return dataWatcherObjectConstructor.invoke(index, field.get(null));
+ } catch (IllegalAccessException e) {
+ throw new SecurityException("Could not get field", e);
+ }
+ }
+ }
+ throw new SecurityException("Could not find Serializer for " + type.getName());
+ }
+
+ private static final Class> spawnLivingPacket = Reflection.getClass("{nms}.PacketPlayOutSpawnEntityLiving");
+ private static final Reflection.ConstructorInvoker spawnLivingPacketConstructor = Reflection.getConstructor(spawnLivingPacket);
+ private static final Reflection.FieldAccessor spawnLivingEntityId = Reflection.getField(spawnLivingPacket, int.class, 0);
+ private static final Reflection.FieldAccessor spawnLivingUUID = Reflection.getField(spawnLivingPacket, UUID.class, 0);
+ private static final Reflection.FieldAccessor spawnLivingEntityType = Reflection.getField(spawnLivingPacket, int.class, 1);
+ private static final Reflection.FieldAccessor spawnLivingEntityX = Reflection.getField(spawnLivingPacket, double.class, 0);
+ private static final Reflection.FieldAccessor spawnLivingEntityY = Reflection.getField(spawnLivingPacket, double.class, 1);
+ private static final Reflection.FieldAccessor spawnLivingEntityZ = Reflection.getField(spawnLivingPacket, double.class, 2);
+
+ private static final Class> metadataPacket = Reflection.getClass("{nms}.PacketPlayOutEntityMetadata");
+ private static final Reflection.ConstructorInvoker metadataConstructor = Reflection.getConstructor(metadataPacket);
+ private static final Reflection.FieldAccessor metadataEntity = Reflection.getField(metadataPacket, int.class, 0);
+ private static final Reflection.FieldAccessor metadataMetadata = Reflection.getField(metadataPacket, List.class, 0);
+ private static final Class> item = Reflection.getClass("{nms}.DataWatcher$Item");
+ private static final Reflection.ConstructorInvoker itemConstructor = Reflection.getConstructor(item, dataWatcherObject, Object.class);
+ private static final Object invisibleWatcher = getDataWatcherObject(0, Byte.class);
+ private static final Object nameWatcher = getDataWatcherObject(2, String.class);
+ private static final Object nameVisibleWatcher = getDataWatcherObject(3, String.class);
+ private static final Object silentWatcher = getDataWatcherObject(4, Boolean.class);
+ private static final Object noGravityWatcher = getDataWatcherObject(5, Boolean.class);
+ private static final Object sizeWatcher = getDataWatcherObject(15, Byte.class);
+
+ private static final Class> destroyPacket = Reflection.getClass("{nms}.PacketPlayOutEntityDestroy");
+ private static final Reflection.ConstructorInvoker destoryPacketConstructor = Reflection.getConstructor(destroyPacket);
+ private static final Reflection.FieldAccessor destroyIds = Reflection.getField(destroyPacket, int[].class, 0);
+
+ private static final List holograms = new ArrayList<>();
+ private static int entityIds = -1;
+ private static final Random random = new Random();
+
+ public static List getHolograms() {
+ return holograms;
+ }
+
+ private final Displayable display;
+ private final int entityId;
+ private final Object spawnLiving;
+ private final Object destroy;
+ private final String id;
+ private final Location location;
+ private final String text;
+ private final boolean persistent;
+
+ public Hologram(Map map) {
+ this((String) map.get("id"), (Location) map.get("location"), (String) map.get("text"), true);
+ }
+
+ public Hologram(String id, Location location, String text, boolean persistent) {
+ this.id = id;
+ this.location = location;
+ this.text = text;
+ this.persistent = persistent;
+ entityId = entityIds--;
+
+ spawnLiving = spawnLivingPacketConstructor.invoke();
+ spawnLivingEntityId.set(spawnLiving, entityId);
+ spawnLivingUUID.set(spawnLiving, new UUID(random.nextLong() & -61441L | 16384L, random.nextLong() & 4611686018427387903L | -9223372036854775808L));
+ spawnLivingEntityType.set(spawnLiving, 1);
+ spawnLivingEntityX.set(spawnLiving, location.getX());
+ spawnLivingEntityY.set(spawnLiving, location.getY());
+ spawnLivingEntityZ.set(spawnLiving, location.getZ());
+
+ destroy = destoryPacketConstructor.invoke();
+ destroyIds.set(destroy, new int[]{entityId});
+
+ display = new Displayable(location, this::show, this::hide);
+
+ if(persistent)
+ holograms.add(this);
+ }
+
+ private void show(Player player) {
+ Protocol.getTinyProtocol().sendPacket(player, spawnLiving);
+
+ Object packet = metadataConstructor.invoke();
+ metadataEntity.set(packet, entityId);
+ List