diff --git a/README.MD b/README.MD index 6bfeecd..508a146 100644 --- a/README.MD +++ b/README.MD @@ -62,6 +62,15 @@ YamlConfiguration config = GhastTools.loadConfig(false); Если передать параметр `false`, то при отсутствии файла `config.yml` в папке плагина, будет загруден исключительно встроенный файл настроек. +### copyLocation + +Копирования объекта `Location` + +```java +Location location = ...; +Location copyLoc = GhastTools.copyLocation(location); +``` + ## AssetsManager Методы по работе с файлами плагина (_"ассетами"_). @@ -110,11 +119,45 @@ _По-умолчанию равен `StandardCharsets.UTF_8`_ ### placeSkull +Установка черепа. + ```java Location location = ...; -Skull skull = BuildHelper.placeSkull(location, BlockFace.NORTH) +Skull skull = BuildHelper.placeSkull(location, BlockFace.NORTH); +skull.update(true); // иначе изменения на карте не применятся и череп будет висеть в воздухе ``` +### placePlayerHead + +Установка головы игрока. + +```java +Location location = ...; +Skull playerHead = BuildHelper.placePlayerHead(location, BlockFace.NORTH); +playerHead.update(true); // иначе изменения на карте не применятся и голова будет висеть в воздухе +``` + +Если третьим параметром передать URL текстуры, то голова будет текстурирована. + +```java +Location location = ...; +BuildHelper.placePlayerHead(location, BlockFace.NORTH, "http://..."); +``` + +### setPlayerHeadSkin + +Установка текстуры для головы игрока. + +```java +Location location = ...; +Skull playerHead = BuildHelper.placePlayerHead(location, BlockFace.NORTH); +playerHead.update(true); // иначе изменения на карте не применятся и голова будет висеть в воздухе +setPlayerHeadSkin(playerHead); +``` + +Порядок выполнения методов в приведённом выше примере **важен**. Если `playerHead.update(true)` вызвать после +установки текстуры, она собъётся на стандартную. + ### placeSignWall ```java diff --git a/buildSrc/src/main/groovy/libs/LibsExtention.groovy b/buildSrc/src/main/groovy/libs/LibsExtention.groovy index 9c104da..938030e 100644 --- a/buildSrc/src/main/groovy/libs/LibsExtention.groovy +++ b/buildSrc/src/main/groovy/libs/LibsExtention.groovy @@ -5,7 +5,7 @@ class LibsExtention { final def commons_text = 'org.apache.commons:commons-text:1.9' final def lombok = 'org.projectlombok:lombok:1.18.12' - final def refobj = 'ru.dmitriymx:reflection-object:1.0-BETA' + final def refobj = 'ru.dmitriymx:reflection-object:1.2' final def bukkit = filter([ lib : 'org.bukkit:bukkit:1.12.2-R0.1-SNAPSHOT', diff --git a/tools/src/main/java/ghast/BuildHelper.java b/tools/src/main/java/ghast/BuildHelper.java index f2b795c..70d97f4 100644 --- a/tools/src/main/java/ghast/BuildHelper.java +++ b/tools/src/main/java/ghast/BuildHelper.java @@ -3,26 +3,111 @@ package ghast; import lombok.experimental.UtilityClass; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.SkullType; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.Sign; import org.bukkit.block.Skull; +import ru.dmitriymx.reflection.ReflectionClass; +import ru.dmitriymx.reflection.ReflectionObject; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.UUID; @UtilityClass @SuppressWarnings("unused") public class BuildHelper { + private final Class> CLASS_BLOCKPOSITION = getClassForName("net.minecraft.server.v1_12_R1.BlockPosition"); + private final Class> CLASS_GAMEPROFILE = getClassForName("com.mojang.authlib.GameProfile"); + + /** + * Установка черепа. + *
+ * После установки, необходимо выполнить skull.update(true);
+ *
+ * @param location место установки.
+ * @param face куда будет повёрнут череп.
+ * @return Блок типа {@link Skull}
+ */
public Skull placeSkull(Location location, BlockFace face) {
+ Location fixedLocation = GhastTools.copyLocation(location);
+ fixedLocation.setZ(fixedLocation.getZ() - 1);
+
Block block = location.getWorld().getBlockAt(location);
block.setType(Material.SKULL);
+
Skull skull = (Skull) block.getState();
skull.setRotation(face);
+
org.bukkit.material.Skull skullMaterial = (org.bukkit.material.Skull) skull.getData();
skullMaterial.setFacingDirection(BlockFace.SELF);
return skull;
}
+ /**
+ * Установка головы игрока.
+ *
+ * После установки, необходимо выполнить skull.update(true);
+ *
+ * @param location место установки.
+ * @param face куда будет повёрнута голова.
+ * @return Блок типа {@link Skull}
+ */
+ public static Skull placePlayerHead(Location location, BlockFace face) {
+ Location fixedLocation = GhastTools.copyLocation(location);
+ fixedLocation.setZ(fixedLocation.getZ() - 1);
+
+ Block block = fixedLocation.getWorld().getBlockAt(fixedLocation);
+ block.setType(Material.SKULL);
+
+ Skull skull = (Skull) block.getState();
+ skull.setSkullType(SkullType.PLAYER);
+ skull.setRotation(face);
+
+ org.bukkit.material.Skull skullMaterial = (org.bukkit.material.Skull) skull.getData();
+ skullMaterial.setFacingDirection(BlockFace.SELF);
+ return skull;
+ }
+
+ /**
+ * Установка текстурированной головы игрока.
+ *
+ * @param location место установки.
+ * @param face куда будет повёрнута голова.
+ * @param skinUrl URL на текстуру
+ * @return Блок типа {@link Skull}
+ */
+ public static Skull placePlayerHead(Location location, BlockFace face, String skinUrl) {
+ Skull playerHead = placePlayerHead(location, face);
+ playerHead.update(true);
+ setPlayerHeadSkin(playerHead, skinUrl);
+
+ return playerHead;
+ }
+
+ /**
+ * Установка текстуры для головы игрока.
+ *
+ * @param skull блок головы игрока
+ * @param skinUrl URL на текстуру
+ */
+ public static void setPlayerHeadSkin(Skull skull, String skinUrl) {
+ //TODO заменить рефлексию на "фантомные" классы
+ ReflectionObject refobjBlockPosition = new ReflectionClass(CLASS_BLOCKPOSITION)
+ .constructor(double.class, double.class, double.class)
+ .newInstance(skull.getX(), skull.getY(), skull.getZ());
+
+ new ReflectionObject(skull.getWorld())
+ .method("getHandle").invoke()
+ .method("getTileEntity", CLASS_BLOCKPOSITION)
+ .invoke(refobjBlockPosition.getOriginalObject())
+ .method("setGameProfile", CLASS_GAMEPROFILE)
+ .invoke(getRefObjPlayerProfile(skinUrl).getOriginalObject());
+ }
+
public Sign placeSignWall(Location location, BlockFace face) {
Block block = location.getWorld().getBlockAt(location);
block.setType(Material.WALL_SIGN);
@@ -33,4 +118,30 @@ public class BuildHelper {
return sign;
}
+
+ private ReflectionObject getRefObjPlayerProfile(String url){
+ ReflectionObject refobjProperty = new ReflectionClass(
+ getClassForName("com.mojang.authlib.properties.Property"))
+ .constructor(String.class, String.class)
+ .newInstance("textures", Base64.getEncoder()
+ .encodeToString(("{textures:{SKIN:{url:\"" + url + "\"}}}").getBytes(StandardCharsets.UTF_8)));
+
+ ReflectionObject refobjGameProfile = new ReflectionClass(CLASS_GAMEPROFILE)
+ .constructor(UUID.class, String.class)
+ .newInstance(UUID.randomUUID(), null);
+ refobjGameProfile
+ .method("getProperties").invoke()
+ .method("put", Object.class, Object.class)
+ .invoke("textures", refobjProperty.getOriginalObject());
+
+ return refobjGameProfile;
+ }
+
+ private Class> getClassForName(String className) {
+ try {
+ return Class.forName(className);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/tools/src/main/java/ghast/GhastTools.java b/tools/src/main/java/ghast/GhastTools.java
index 9ce291e..df67957 100644
--- a/tools/src/main/java/ghast/GhastTools.java
+++ b/tools/src/main/java/ghast/GhastTools.java
@@ -2,6 +2,7 @@ package ghast;
import ghast.assets.AssetsManager;
import lombok.experimental.UtilityClass;
+import org.bukkit.Location;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
@@ -54,4 +55,8 @@ public class GhastTools {
public YamlConfiguration loadConfig() {
return loadConfig(true);
}
+
+ public Location copyLocation(Location location) {
+ return new Location(location.getWorld(), location.getX(), location.getY(), location.getZ());
+ }
}