use of com.bergerkiller.generated.net.minecraft.world.entity.decoration.EntityItemFrameHandle in project BKCommonLib by bergerhealer.
the class CommonMapController method initItemFrameSetOfWorld.
private final List<EntityItemFrameHandle> initItemFrameSetOfWorld(World world) {
List<EntityItemFrameHandle> itemFrames = new ArrayList<EntityItemFrameHandle>();
for (Object entityHandle : (Iterable<?>) WorldServerHandle.T.getEntities.raw.invoke(HandleConversion.toWorldHandle(world))) {
if (EntityItemFrameHandle.T.isAssignableFrom(entityHandle)) {
EntityItemFrameHandle itemFrame = EntityItemFrameHandle.createHandle(entityHandle);
getItemFrameEntities(new ItemFrameClusterKey(itemFrame)).add(itemFrame);
itemFrames.add(itemFrame);
}
}
return itemFrames;
}
use of com.bergerkiller.generated.net.minecraft.world.entity.decoration.EntityItemFrameHandle in project BKCommonLib by bergerhealer.
the class CommonMapController method findCluster.
/**
* Finds a cluster of all connected item frames that an item frame is part of
*
* @param itemFrame Start item frame
* @param itemFramePosition Start block position of itemFrame
* @param includingEmpty Whether to include frames in the cluster that have no item
* @return cluster
*/
public final synchronized ItemFrameCluster findCluster(final EntityItemFrameHandle itemFrame, final IntVector3 itemFramePosition, final boolean includingEmpty) {
final Predicate<EntityItemFrameHandle> itemFrameFilter;
if (includingEmpty && ItemUtil.isEmpty(itemFrame.getItem())) {
// Proceed only filling empty item frames
itemFrameFilter = e -> ItemUtil.isEmpty(e.getItem());
} else {
final UUID itemFrameMapUUID;
if (this.isFrameTilingSupported && (itemFrameMapUUID = itemFrame.getItemMapDisplayDynamicOnlyUUID()) != null) {
if (includingEmpty) {
itemFrameFilter = e -> {
return itemFrameMapUUID.equals(e.getItemMapDisplayDynamicOnlyUUID()) || ItemUtil.isEmpty(e.getItem());
};
} else {
itemFrameFilter = e -> {
return itemFrameMapUUID.equals(e.getItemMapDisplayDynamicOnlyUUID());
};
}
} else {
// no neighbours or tiling disabled
return new ItemFrameCluster(OfflineWorld.of(itemFrame.getBukkitWorld()), itemFrame.getFacing(), Collections.singleton(itemFramePosition), 0);
}
}
// Look up in cache first
World world = itemFrame.getBukkitWorld();
Map<IntVector3, ItemFrameCluster> cachedClusters;
if (!includingEmpty && itemFrameClustersByWorldEnabled) {
cachedClusters = itemFrameClustersByWorld.get(world);
if (cachedClusters == null) {
cachedClusters = new HashMap<>();
itemFrameClustersByWorld.put(world, cachedClusters);
}
ItemFrameCluster fromCache = cachedClusters.get(itemFramePosition);
if (fromCache != null) {
return fromCache;
}
} else {
cachedClusters = null;
}
// Take cache entry
FindNeighboursCache cache = this.findNeighboursCache;
if (cache != null) {
cache.reset();
this.findNeighboursCache = null;
} else {
cache = new FindNeighboursCache();
}
try {
// Find all item frames that:
// - Are on the same world as this item frame
// - Facing the same way
// - Along the same x/y/z (facing)
// - Same ItemStack map UUID (or, if includingEmpty, are empty)
ItemFrameClusterKey key = new ItemFrameClusterKey(world, itemFrame.getFacing(), itemFramePosition);
for (EntityItemFrameHandle otherFrame : getItemFrameEntities(key)) {
if (otherFrame.getId() != itemFrame.getId() && itemFrameFilter.test(otherFrame)) {
cache.put(otherFrame);
}
}
BlockFace[] neighbourAxis;
if (FaceUtil.isAlongY(key.facing)) {
neighbourAxis = NEIGHBOUR_AXIS_ALONG_Y;
} else if (FaceUtil.isAlongX(key.facing)) {
neighbourAxis = NEIGHBOUR_AXIS_ALONG_X;
} else {
neighbourAxis = NEIGHBOUR_AXIS_ALONG_Z;
}
// Find the most common item frame rotation in use
// Only 4 possible rotations can be used for maps, so this is easy
int[] rotation_counts = new int[4];
rotation_counts[(new FindNeighboursCache.Frame(itemFrame)).rotation]++;
// Make sure the neighbours result are a single contiguous blob
// Islands (can not reach the input item frame) are removed
Set<IntVector3> result = new HashSet<IntVector3>(cache.cache.size());
result.add(itemFramePosition);
cache.pendingList.add(itemFramePosition);
do {
IntVector3 pending = cache.pendingList.poll();
for (BlockFace side : neighbourAxis) {
IntVector3 sidePoint = pending.add(side);
FindNeighboursCache.Frame frame = cache.cache.remove(sidePoint);
if (frame != null) {
rotation_counts[frame.rotation]++;
cache.pendingList.add(sidePoint);
result.add(sidePoint);
}
}
} while (!cache.pendingList.isEmpty());
// Find maximum rotation index
int rotation_idx = 0;
for (int i = 1; i < rotation_counts.length; i++) {
if (rotation_counts[i] > rotation_counts[rotation_idx]) {
rotation_idx = i;
}
}
// The final combined result
ItemFrameCluster cluster = new ItemFrameCluster(OfflineWorld.of(key.world), key.facing, result, rotation_idx * 90);
if (cachedClusters != null) {
for (IntVector3 position : cluster.coordinates) {
cachedClusters.put(position, cluster);
}
}
return cluster;
} finally {
// Return to cache
this.findNeighboursCache = cache;
}
}
use of com.bergerkiller.generated.net.minecraft.world.entity.decoration.EntityItemFrameHandle in project BKCommonLib by bergerhealer.
the class MapDisplayItemMapIdUpdater method updateMapIds.
public void updateMapIds() {
// Remove non-existing maps from the internal mapping
if (controller.idGenerationCounter > GENERATION_COUNTER_CLEANUP_INTERVAL) {
controller.idGenerationCounter = 0;
// Find all map UUIDs that exist on the server
HashSet<MapUUID> validUUIDs = new HashSet<MapUUID>();
for (Set<EntityItemFrameHandle> itemFrameSet : controller.itemFrameEntities.values()) {
for (EntityItemFrameHandle itemFrame : itemFrameSet) {
MapUUID mapUUID = controller.getItemFrameMapUUID(itemFrame);
if (mapUUID != null) {
validUUIDs.add(mapUUID);
}
}
}
for (Player player : Bukkit.getOnlinePlayers()) {
PlayerInventory inv = player.getInventory();
for (int i = 0; i < inv.getSize(); i++) {
ItemStack item = inv.getItem(i);
UUID mapUUID = CommonMapUUIDStore.getMapUUID(item);
if (mapUUID != null) {
validUUIDs.add(new MapUUID(mapUUID));
}
}
}
// Perform the cleanup (synchronized access required!)
controller.cleanupUnusedUUIDs(validUUIDs);
}
// Refresh items known to clients when Map Ids are re-assigned
// Swap around the tmp and main set every tick
final SetMultimap<UUID, MapUUID> dirtyMaps = controller.swapDirtyMapUUIDs();
if (!dirtyMaps.isEmpty()) {
// This will result in new SetItemSlot packets being sent, refreshing the map Id
for (Player player : Bukkit.getOnlinePlayers()) {
PlayerInventory inv = player.getInventory();
for (int i = 0; i < inv.getSize(); i++) {
ItemStack item = inv.getItem(i);
UUID uuid = CommonMapUUIDStore.getMapUUID(item);
if (dirtyMaps.containsKey(uuid)) {
inv.setItem(i, item.clone());
}
}
}
// Refresh all item frames that display this map
// This will result in a new EntityMetadata packets being sent, refreshing the map Id
// After updating all item frames, resend the maps
dirtyMaps.keySet().stream().map(controller.maps::get).filter(Objects::nonNull).forEach(info -> {
// Refresh item of all affected item frames
// This re-sends metadata packets
final Set<MapUUID> mapUUIDs = dirtyMaps.get(info.getUniqueId());
for (ItemFrameInfo itemFrameInfo : info.getItemFrames()) {
if (mapUUIDs.contains(itemFrameInfo.lastMapUUID)) {
itemFrameInfo.itemFrameHandle.refreshItem();
}
}
// Resend map data for all affected tiles
for (MapSession session : info.getSessions()) {
for (MapDisplayTile tile : session.tiles) {
if (mapUUIDs.contains(tile.getMapTileUUID())) {
session.onlineOwners.forEach(o -> o.sendDirtyTile(tile));
}
}
}
});
// Done processing, wipe
dirtyMaps.clear();
}
}
use of com.bergerkiller.generated.net.minecraft.world.entity.decoration.EntityItemFrameHandle in project BKCommonLib by bergerhealer.
the class CommonMapController method onEntityRemoved.
@EventHandler(priority = EventPriority.MONITOR)
protected synchronized void onEntityRemoved(EntityRemoveEvent event) {
if (event.getEntity() instanceof ItemFrame) {
ItemFrame frame = (ItemFrame) event.getEntity();
EntityItemFrameHandle frameHandle = EntityItemFrameHandle.fromBukkit(frame);
getItemFrameEntities(new ItemFrameClusterKey(frameHandle)).remove(frameHandle);
ItemFrameInfo info = itemFrames.get(frame.getEntityId());
if (info != null) {
info.removed = true;
info.needsItemRefresh.set(false);
}
}
}
use of com.bergerkiller.generated.net.minecraft.world.entity.decoration.EntityItemFrameHandle in project BKCommonLib by bergerhealer.
the class CommonMapController method onEnable.
/**
* Starts all continuous background update tasks for maps
*
* @param plugin
* @param startedTasks
*/
public void onEnable(CommonPlugin plugin, List<Task> startedTasks) {
this.isFrameTilingSupported = plugin.isFrameTilingSupported();
this.isFrameDisplaysEnabled = plugin.isFrameDisplaysEnabled();
plugin.register((Listener) this);
plugin.register((PacketListener) this, PACKET_TYPES);
plugin.register(new MapDisplayItemChangeListener(this));
startedTasks.add(new MapDisplayHeldMapUpdater(plugin, this).start(1, 1));
startedTasks.add(new MapDisplayItemMapIdUpdater(plugin, this).start(1, 1));
startedTasks.add(new MapDisplayInputUpdater(plugin, this).start(1, 1));
startedTasks.add(new MapDisplayCreativeDraggedMapItemCleaner(plugin, this).start(100, CreativeDraggedMapItem.CACHED_ITEM_CLEAN_INTERVAL));
// These tasks only run when map displays on item frames are enabled
if (this.isFrameDisplaysEnabled) {
startedTasks.add(new MapDisplayFramedMapUpdater(plugin, this).start(1, 1));
// every minute
startedTasks.add(new ByWorldItemFrameSetRefresher(plugin).start(1200, 1200));
}
// Whether this sort of stuff is relevant at all, in case it's set from somewhere
// Avoids a memory leak
this.mapsWithItemFrameResolutionChanges.setEnabled(this.isFrameDisplaysEnabled && this.isFrameTilingSupported);
this.mapsWithItemFrameViewerChanges.setEnabled(this.isFrameDisplaysEnabled);
this.itemFramesThatNeedItemRefresh.setEnabled(this.isFrameDisplaysEnabled);
// No actual initialization is done yet, this happens next tick cycle!
if (this.isFrameDisplaysEnabled) {
for (World world : Bukkit.getWorlds()) {
for (EntityItemFrameHandle itemFrame : initItemFrameSetOfWorld(world)) {
onAddItemFrame(itemFrame);
}
}
}
// This ensures updated map details are refreshed during the item update discovery
if (CommonUtil.getServerTicks() > 0) {
this.getItemFrames().forEach(info -> {
if (CommonMapUUIDStore.isMap(getItemFrameItem(info.itemFrame))) {
info.sentMapInfoToPlayers = true;
}
});
}
// If this is a reload, that means players have already been watching maps potentially
// To minimize glitches and problems, restore the map id data from last run
CommonMapReloadFile.load(plugin, reloadFile -> {
// Static reserved ids (other plugins have been using it)
for (Integer staticId : reloadFile.staticReservedIds) {
storeStaticMapId(staticId.intValue());
}
// To avoid 'popping', make sure to pre-cache the same ones
for (CommonMapReloadFile.DynamicMappedId dynamicMapId : reloadFile.dynamicMappedIds) {
if (mapUUIDById.contains(dynamicMapId.id)) {
// Already assigned, skip
continue;
}
if (mapIdByUUID.containsKey(dynamicMapId.uuid)) {
// Already assigned, skip
continue;
}
// Store
mapIdByUUID.put(dynamicMapId.uuid, dynamicMapId.id);
mapUUIDById.put(dynamicMapId.id, dynamicMapId.uuid);
}
// Give a hint about Map UUID to avoid 'popping' when the item is refreshed
for (CommonMapReloadFile.ItemFrameDisplayUUID displayUUID : reloadFile.itemFrameDisplayUUIDs) {
ItemFrameInfo itemFrame = itemFrames.get(displayUUID.entityId);
if (itemFrame != null) {
itemFrame.preReloadMapUUID = displayUUID.uuid;
}
}
});
// Done!
this.isEnabled = true;
}
Aggregations