ArmorStand Item Display For Chests!

Chests will now display the block/item that is most common inside the chest!
Involves a rework of the save file that needs to be tested.
This commit is contained in:
jameslfc19 2020-06-28 21:59:57 +01:00
parent 341e12db75
commit 383657dad7
5 changed files with 265 additions and 34 deletions

View File

@ -15,6 +15,7 @@ import com.jamesdpeters.minecraft.chests.serialize.AutoCraftingStorage;
import com.jamesdpeters.minecraft.chests.serialize.Config;
import com.jamesdpeters.minecraft.chests.serialize.InventoryStorage;
import com.jamesdpeters.minecraft.chests.serialize.LinkedChest;
import com.jamesdpeters.minecraft.chests.serialize.LocationInfo;
import com.jamesdpeters.minecraft.chests.serialize.MaterialSerializer;
import com.jamesdpeters.minecraft.chests.serialize.RecipeSerializable;
import com.jamesdpeters.minecraft.chests.serialize.SpigotConfig;
@ -63,6 +64,7 @@ public class ChestsPlusPlus extends JavaPlugin {
ConfigurationSerialization.registerClass(MaterialSerializer.class, "Material");
ConfigurationSerialization.registerClass(AutoCraftingStorage.class, "AutoCraftingStorage");
ConfigurationSerialization.registerClass(RecipeSerializable.class, "Recipe");
ConfigurationSerialization.registerClass(LocationInfo.class, "LocationInfo");
}
@SuppressWarnings("ConstantConditions")

View File

@ -4,6 +4,7 @@ import com.jamesdpeters.minecraft.chests.ChestsPlusPlus;
import com.jamesdpeters.minecraft.chests.filters.HopperFilter;
import com.jamesdpeters.minecraft.chests.misc.Utils;
import com.jamesdpeters.minecraft.chests.serialize.InventoryStorage;
import com.jamesdpeters.minecraft.chests.serialize.LocationInfo;
import com.jamesdpeters.minecraft.chests.serialize.SpigotConfig;
import org.bukkit.Location;
import org.bukkit.block.Hopper;
@ -29,17 +30,19 @@ public class VirtualChestToHopper extends BukkitRunnable {
@Override
public void run() {
for(Location location : storage.getLocations()) {
for(LocationInfo location : storage.getLocations()) {
if(location != null) {
Location below = location.clone().subtract(0, 1, 0);
if (below.getBlock().getState() instanceof Hopper) {
Hopper hopper = (Hopper) below.getBlock().getState();
if(below.getBlock().isBlockIndirectlyPowered()|| below.getBlock().isBlockPowered()){
continue;
if (location.getLocation() != null) {
Location below = location.getLocation().clone().subtract(0, 1, 0);
if (below.getBlock().getState() instanceof Hopper) {
Hopper hopper = (Hopper) below.getBlock().getState();
if (below.getBlock().isBlockIndirectlyPowered() || below.getBlock().isBlockPowered()) {
continue;
}
int hopperAmount = SpigotConfig.getWorldSettings(location.getLocation().getWorld().getName()).getHopperAmount();
Utils.moveToOtherInventory(storage.getInventory(), hopperAmount, hopper.getInventory(), HopperFilter.getHopperFilters(below.getBlock()));
storage.sort();
}
int hopperAmount = SpigotConfig.getWorldSettings(location.getWorld().getName()).getHopperAmount();
Utils.moveToOtherInventory(storage.getInventory(), hopperAmount , hopper.getInventory(), HopperFilter.getHopperFilters(below.getBlock()));
storage.sort();
}
}
}

View File

@ -84,7 +84,7 @@ public class Config {
Chest chest = (Chest) block.getState();
ChestLinkInfo info = Utils.getChestLinkInfo(chest.getLocation());
if (info != null) {
return info.getStorage();
return info.getStorage(location);
}
}
}
@ -123,8 +123,8 @@ public class Config {
chest.getInventory().clear();
//If the location isn't already part of the system add it.
if (!inventoryStorage.getLocations().contains(chestLocation)) {
inventoryStorage.getLocations().add(chestLocation);
if (!inventoryStorage.containsLocation(chestLocation)) {
inventoryStorage.addLocation(chestLocation);
}
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1f);
saveASync();
@ -134,8 +134,8 @@ public class Config {
public static InventoryStorage removeChest(InventoryStorage storage, Location location) {
if (storage != null) {
storage.getLocations().remove(location);
if (storage.getLocations().size() == 0) {
storage.removeLocation(location);
if (storage.getLocationsSize() == 0) {
storage.dropInventory(location);
getInventoryStorageMap(storage.getOwner().getUniqueId()).remove(storage.getIdentifier());
}
@ -150,7 +150,7 @@ public class Config {
if (storage != null) {
storage.getLocations().forEach(location -> {
if (location != null) {
Block block = location.getBlock();
Block block = location.getLocation().getBlock();
block.breakNaturally();
}
});

View File

@ -14,42 +14,54 @@ import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.Chest;
import org.bukkit.block.Sign;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.util.EulerAngle;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
public class InventoryStorage implements ConfigurationSerializable {
Inventory inventory; //Old Inventory
ArrayList<Location> locationsList;
ArrayList<String> members; //Members UUID
List<OfflinePlayer> bukkitMembers;
String inventoryName = "Chest";
VirtualChestToHopper chestToHopper;
OfflinePlayer player;
UUID playerUUID;
boolean isPublic;
SortMethod sortMethod;
private Inventory inventory; //Old Inventory
// ArrayList<Location> locationsList;
private ArrayList<String> members; //Members UUID
private List<OfflinePlayer> bukkitMembers;
private String inventoryName = "Chest";
private VirtualChestToHopper chestToHopper;
private OfflinePlayer player;
private UUID playerUUID;
private boolean isPublic;
private SortMethod sortMethod;
private List<LocationInfo> locationInfoList;
@Override
public Map<String, Object> serialize() {
LinkedHashMap<String, Object> hashMap = new LinkedHashMap<>();
hashMap.put("inventory",inventory.getContents());
hashMap.put("locations",locationsList);
hashMap.put("locationInfo",locationInfoList);
hashMap.put("inventoryName",inventoryName);
hashMap.put("playerUUID",playerUUID.toString());
hashMap.put("members", members);
@ -67,8 +79,14 @@ public class InventoryStorage implements ConfigurationSerializable {
ItemStack[] itemStacks = ((ArrayList<ItemStack>) map.get("inventory")).toArray(new ItemStack[0]);
inventory.setContents(itemStacks);
locationsList = (ArrayList<Location>) map.get("locations");
locationsList.removeAll(Collections.singletonList(null));
List<Location> locations = (ArrayList<Location>) map.get("locations");
if(locations != null) {
locations.removeAll(Collections.singletonList(null));
locationInfoList = LocationInfo.convert(locations);
} else {
locationInfoList = (List<LocationInfo>) map.get("locationInfo");
locationInfoList.removeAll(Collections.singletonList(null));
}
playerUUID = UUID.fromString((String) map.get("playerUUID"));
player = Bukkit.getOfflinePlayer(playerUUID);
@ -96,7 +114,8 @@ public class InventoryStorage implements ConfigurationSerializable {
this.playerUUID = player.getUniqueId();
this.isPublic = false;
this.sortMethod = SortMethod.OFF;
locationsList = new ArrayList<>(Collections.singleton(location));
LocationInfo locationInfo = new LocationInfo(location);
locationInfoList = new ArrayList<>(Collections.singleton(locationInfo));
Block block = location.getBlock();
if(block.getState() instanceof Chest){
@ -112,6 +131,7 @@ public class InventoryStorage implements ConfigurationSerializable {
private void init(){
chestToHopper = new VirtualChestToHopper(this);
chestToHopper.start();
Bukkit.getScheduler().scheduleSyncRepeatingTask(ChestsPlusPlus.PLUGIN, this::updatePlayers, 1, 5);
}
private Inventory initInventory(){
@ -122,8 +142,36 @@ public class InventoryStorage implements ConfigurationSerializable {
return inventory;
}
public List<Location> getLocations() {
return locationsList;
/**
* @return List of locations
* *THIS MUST NOT BE OPERATED ON SINCE ARMOR STANDS DEPEND ON THE PROPER REMOVAL OF LOCATIONS.
*/
public List<LocationInfo> getLocations() {
return locationInfoList;
}
public void addLocation(Location location){
locationInfoList.add(new LocationInfo(location));
}
public void removeLocation(Location location){
//Remove armor stands from the world.
locationInfoList.stream().filter(locationInfo -> locationInfo.getLocation().equals(location)).forEach(locationInfo -> {
ArmorStand blockStand = locationInfo.getBlockStand();
if(blockStand != null) blockStand.remove();
ArmorStand itemStand = locationInfo.getItemStand();
if(itemStand != null) itemStand.remove();
});
//Remove this location from storage.
locationInfoList.removeIf(locationInfo -> locationInfo.getLocation().equals(location));
}
public boolean containsLocation(Location location){
return locationInfoList.stream().anyMatch(locationInfo -> locationInfo.getLocation().equals(location));
}
public int getLocationsSize(){
return locationInfoList.size();
}
public void dropInventory(Location location){
@ -147,7 +195,7 @@ public class InventoryStorage implements ConfigurationSerializable {
@Override
public String toString() {
return inventoryName+": "+locationsList.toString();
return inventoryName+": "+locationInfoList.toString();
}
public ItemStack getIventoryIcon(Player player){
@ -232,8 +280,8 @@ public class InventoryStorage implements ConfigurationSerializable {
ItemStack[] items = inventory.getContents();
inventory = initInventory();
inventory.setContents(items);
locationsList.forEach(location -> {
Block block = location.getBlock();
locationInfoList.forEach(location -> {
Block block = location.getLocation().getBlock();
if(block.getBlockData() instanceof org.bukkit.block.data.type.Chest) {
org.bukkit.block.data.type.Chest chest = (org.bukkit.block.data.type.Chest) block.getBlockData();
BlockFace blockFace = chest.getFacing();
@ -245,6 +293,107 @@ public class InventoryStorage implements ConfigurationSerializable {
});
}
private void updatePlayers(){
for (LocationInfo location : locationInfoList) {
World world = location.getLocation().getWorld();
Block block = location.getLocation().getBlock();
BlockData air = Material.AIR.createBlockData();
if(world != null) {
Collection<Entity> players = world.getNearbyEntities(location.getLocation(), 20, 20, 20, entity -> entity instanceof Player);
players.forEach(entity -> {
if(entity instanceof Player){
Player player = (Player) entity;
if(block.getBlockData() instanceof Directional) {
Directional chest = (Directional) block.getBlockData();
BlockFace facing = chest.getFacing();
Block sign = block.getRelative(facing);
ItemStack mostCommon = InventorySorter.getMostCommonItem(inventory);
if(mostCommon != null) {
boolean isBlock = mostCommon.getType().isBlock();
Location standLoc = getArmorStandLoc(sign,facing, mostCommon.getType().isBlock());
//Make client think sign is invisible.
player.sendBlockChange(sign.getLocation(), air);
//Get currently stored armorStand if there isn't one spawn it.
ArmorStand stand = isBlock ? location.getBlockStand() : location.getItemStand();
if(stand == null || !stand.isValid()){
stand = createArmorStand(world,standLoc);
addArmorStand(isBlock, location, stand);
}
stand.setHelmet(mostCommon);
//Set on fire to correct lighting.
stand.setFireTicks(Integer.MAX_VALUE);
//Set other armor stand helmet to null.
setArmorStandHelmet(!isBlock, location, null);
} else {
sign.getState().update();
setArmorStandHelmet(true,location,null);
setArmorStandHelmet(false,location,null);
}
}
}
});
}
}
}
private ArmorStand createArmorStand(World world, Location standLoc){
ArmorStand stand = world.spawn(standLoc, ArmorStand.class);
stand.setVisible(false);
stand.setGravity(false);
stand.setSilent(true);
stand.setInvulnerable(true);
stand.setMarker(true);
stand.setBasePlate(false);
stand.setSmall(true);
return stand;
}
private Location getArmorStandLoc(Block sign, BlockFace facing, boolean isBlock){
// int yawOffset = isBlock ? 180 : 0;
double directionFactor = isBlock ? 0.6 : 0.3;
double y = isBlock ? 0.1 : 0.45;
//Get centre of block location.
Location standLoc = sign.getLocation().add(0.5,-0.5,0.5);
Vector direction = facing.getDirection();
standLoc.setYaw(getYaw(direction.getX(),direction.getZ())+180);
return standLoc.subtract(directionFactor*direction.getX(),y, directionFactor*direction.getZ());
}
private void setArmorStandHelmet(boolean isBlock, LocationInfo location, ItemStack helmet){
ArmorStand stand = isBlock ? location.getBlockStand() : location.getItemStand();
if(stand != null) stand.setHelmet(helmet);
}
private void addArmorStand(boolean isBlock, LocationInfo location, ArmorStand stand){
if(isBlock) location.setBlockStand(stand);
else location.setItemStand(stand);
}
/**
* Get yaw based upon the direction of the x and y components of the Chest BlockFace
* Uses precalculated values for most orientations.
* @param x component
* @param y component
* @return yaw
*/
private float getYaw(double x, double y){
if(x == 0 && y == -1) return 0;
if(x == 1 && y == 0) return 90;
if(x == 0 && y == 1) return 180;
if(x == -1 && y == 0) return 270;
return (float) (Math.asin(y/Math.sqrt(y*y+x*x))+90);
}
public List<OfflinePlayer> getMembers(){
return bukkitMembers;
}

View File

@ -0,0 +1,77 @@
package com.jamesdpeters.minecraft.chests.serialize;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.SerializableAs;
import org.bukkit.entity.ArmorStand;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
@SerializableAs("LocationInfo")
public class LocationInfo implements ConfigurationSerializable {
private Location location;
private ArmorStand itemStand, blockStand;
@Override
public Map<String, Object> serialize() {
HashMap<String, Object> map = new HashMap<>();
map.put("Location", location);
map.put("itemStand", itemStand != null ? itemStand.getUniqueId().toString() : "");
map.put("blockStand", blockStand != null ? blockStand.getUniqueId().toString() : "");
return map;
}
public LocationInfo(Map<String, Object> map){
location = (Location) map.get("Location");
UUID itemStandUUID = UUID.fromString((String) map.get("itemStand"));
UUID blockStandUUID = UUID.fromString((String) map.get("blockStand"));
itemStand = (ArmorStand) Bukkit.getServer().getEntity(itemStandUUID);
blockStand = (ArmorStand) Bukkit.getServer().getEntity(blockStandUUID);
}
public LocationInfo(Location location){
this.location = location;
}
public Location getLocation() {
return location;
}
public ArmorStand getBlockStand() {
return blockStand;
}
public ArmorStand getItemStand() {
return itemStand;
}
public LocationInfo setBlockStand(ArmorStand blockStand) {
this.blockStand = blockStand;
return this;
}
public LocationInfo setItemStand(ArmorStand itemStand) {
this.itemStand = itemStand;
return this;
}
public static List<LocationInfo> convert(List<Location> locationList){
List<LocationInfo> locationInfos = new ArrayList<>();
for (Location location : locationList) {
locationInfos.add(new LocationInfo(location));
}
return locationInfos;
}
public static Optional<LocationInfo> getLocationInfo(List<LocationInfo> locationInfos, Location location){
return locationInfos.stream().filter(locationInfo -> locationInfo.getLocation().equals(location)).findFirst();
}
}