Skip to content

Commit

Permalink
Implement Beacon logic (#1092)
Browse files Browse the repository at this point in the history
  • Loading branch information
SHADOWDANCH committed May 7, 2020
1 parent 3c14954 commit c4c59a7
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 25 deletions.
37 changes: 37 additions & 0 deletions src/main/java/net/glowstone/block/blocktype/BlockBeacon.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package net.glowstone.block.blocktype;

import net.glowstone.block.GlowBlock;
import net.glowstone.block.GlowBlockState;
import net.glowstone.block.entity.BeaconEntity;
import net.glowstone.block.entity.BlockEntity;
import net.glowstone.chunk.GlowChunk;
import net.glowstone.entity.GlowPlayer;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.Beacon;
import org.bukkit.block.BlockFace;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;

public class BlockBeacon extends BlockDirectDrops {

Expand All @@ -15,4 +23,33 @@ public BlockBeacon() {
public BlockEntity createBlockEntity(GlowChunk chunk, int cx, int cy, int cz) {
return new BeaconEntity(chunk.getBlock(cx, cy, cz));
}

@Override
public void placeBlock(GlowPlayer player, GlowBlockState state, BlockFace face, ItemStack holding, Vector clickedLoc) {
super.placeBlock(player, state, face, holding, clickedLoc);
state.getBlock().getWorld().requestPulse(state.getBlock());
}

@Override
public boolean blockInteract(GlowPlayer player, GlowBlock block, BlockFace face, Vector clickedLoc) {
Beacon beacon = (Beacon) block.getState();
player.openInventory(beacon.getInventory());
player.incrementStatistic(Statistic.BEACON_INTERACTION);
return true;
}

@Override
public boolean isPulseOnce(GlowBlock block) {
return false;
}

@Override
public int getPulseTickSpeed(GlowBlock block) {
return 80;
}

@Override
public void receivePulse(GlowBlock block) {
((BeaconEntity) block.getBlockEntity()).onPulse();
}
}
121 changes: 114 additions & 7 deletions src/main/java/net/glowstone/block/entity/BeaconEntity.java
Original file line number Diff line number Diff line change
@@ -1,35 +1,137 @@
package net.glowstone.block.entity;

import com.destroystokyo.paper.event.block.BeaconEffectEvent;
import com.google.common.collect.Sets;
import java.util.Set;
import lombok.Getter;
import lombok.Setter;
import net.glowstone.EventFactory;
import net.glowstone.block.GlowBlock;
import net.glowstone.block.entity.state.GlowBeacon;
import net.glowstone.entity.GlowPlayer;
import net.glowstone.util.nbt.CompoundTag;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Beacon;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;

public class BeaconEntity extends BlockEntity {

private static final Set<Material> BEACON_BASE = Sets.newHashSet(
Material.EMERALD_BLOCK, Material.GOLD_BLOCK,
Material.DIAMOND_BLOCK, Material.IRON_BLOCK
);
private static final Set<Material> TRANSPARENT_BLOCKS = Sets.newHashSet(
Material.AIR, Material.GLASS, Material.THIN_GLASS,
Material.STAINED_GLASS, Material.STAINED_GLASS_PANE
);

private String lock = null; // todo: support item locks
@Getter
@Setter
private int levels = 0;
private int levels;
@Getter
@Setter
private int primaryId = 0;
private int primaryEffectId;
@Getter
@Setter
private int secondaryId = 0;
private int secondaryEffectId;

public BeaconEntity(GlowBlock block) {
super(block);
setSaveId("minecraft:beacon");
}

@Override
public GlowBeacon getState() {
return new GlowBeacon(block);
}

@Override
public void update(GlowPlayer player) {
player.setWindowProperty(InventoryView.Property.LEVELS, levels);
player.setWindowProperty(InventoryView.Property.PRIMARY_EFFECT, primaryEffectId);
player.setWindowProperty(InventoryView.Property.SECONDARY_EFFECT, secondaryEffectId);
}

public void onPulse() {
if (canBeEnabled()) {
calculateLevels();
applyPotionEffects();
}
}

private boolean canBeEnabled() {
World world = block.getWorld();
for (int y = block.getY() + 1; y < world.getMaxHeight(); y++) {
if (!TRANSPARENT_BLOCKS.contains(world.getBlockAt(block.getX(), y, block.getZ()).getType())) {
return false;
}
}
return true;
}

private void calculateLevels() {
int beaconX = block.getX();
int beaconY = block.getY();
int beaconZ = block.getZ();
for (int layer = 1; layer <= 4; this.levels = layer++) {
int layerY = beaconY - layer;
if (layerY < 0) {
break;
}
boolean isLayerComplete = true;
for (int x = beaconX - layer; x <= beaconX + layer && isLayerComplete; ++x) {
for (int z = beaconZ - layer; z <= beaconZ + layer; ++z) {
if (!BEACON_BASE.contains(getBlock().getWorld().getBlockAt(x, layerY, z).getType())) {
isLayerComplete = false;
break;
}
}
}
if (!isLayerComplete) {
break;
}
}
}

private void applyPotionEffects() {
Beacon beacon = (Beacon) getBlock().getState();

beacon.getEntitiesInRange().forEach(livingEntity -> {
for (BeaconEffectPriority priority : BeaconEffectPriority.values()) {
PotionEffect effect = this.getEffect(priority);
if (effect == null) {
continue;
}
BeaconEffectEvent event = new BeaconEffectEvent(block, effect, (Player) livingEntity, priority == BeaconEffectPriority.PRIMARY);
if (!EventFactory.getInstance().callEvent(event).isCancelled()) {
livingEntity.addPotionEffect(event.getEffect(), true);
}
}
});
}

public PotionEffect getEffect(BeaconEffectPriority priority) {
PotionEffectType type = PotionEffectType.getById(priority == BeaconEffectPriority.PRIMARY ? primaryEffectId : secondaryEffectId);
if (type == null) {
return null;
}
int effectDuration = (9 + levels * 2) * 20;
int effectAmplifier = levels >= 4 && primaryEffectId == secondaryEffectId ? 1 : 0;
return new PotionEffect(type, effectDuration, effectAmplifier, true, true);
}

@Override
public void loadNbt(CompoundTag tag) {
super.loadNbt(tag);
tag.readString("Lock", lock -> this.lock = lock);
tag.readInt("Levels", this::setLevels);
tag.readInt("Primary", this::setPrimaryId);
tag.readInt("Secondary", this::setSecondaryId);
tag.readInt("Primary", this::setPrimaryEffectId);
tag.readInt("Secondary", this::setSecondaryEffectId);
}

@Override
Expand All @@ -39,7 +141,12 @@ public void saveNbt(CompoundTag tag) {
tag.putString("Lock", lock);
}
tag.putInt("Levels", levels);
tag.putInt("Primary", primaryId);
tag.putInt("Secondary", secondaryId);
tag.putInt("Primary", primaryEffectId);
tag.putInt("Secondary", secondaryEffectId);
}

public enum BeaconEffectPriority {
PRIMARY,
SECONDARY
}
}
35 changes: 17 additions & 18 deletions src/main/java/net/glowstone/block/entity/state/GlowBeacon.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
package net.glowstone.block.entity.state;

import java.util.Collection;
import java.util.Collections;
import lombok.Getter;
import net.glowstone.block.GlowBlock;
import net.glowstone.block.entity.BeaconEntity;
import net.glowstone.inventory.GlowBeaconInventory;
import org.bukkit.block.Beacon;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.BeaconInventory;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;

public class GlowBeacon extends GlowContainer implements Beacon {

@Getter
private PotionEffect primaryEffect;
@Getter
private PotionEffect secondaryEffect;
private static final int RADIUS_MULTIPLIER = 10;

/**
* Creates an entity for the given beacon block.
Expand All @@ -25,12 +22,6 @@ public class GlowBeacon extends GlowContainer implements Beacon {
*/
public GlowBeacon(GlowBlock block) {
super(block);
if (getBlockEntity().getPrimaryId() > 0) {
setPrimaryEffect(PotionEffectType.getById(getBlockEntity().getPrimaryId()));
}
if (getBlockEntity().getSecondaryId() > 0) {
setSecondaryEffect(PotionEffectType.getById(getBlockEntity().getSecondaryId()));
}
}

private BeaconEntity getBlockEntity() {
Expand All @@ -39,7 +30,7 @@ private BeaconEntity getBlockEntity() {

@Override
public Collection<LivingEntity> getEntitiesInRange() {
return Collections.emptyList();
return getWorld().getNearbyEntitiesByType(Player.class, getLocation(), getTier() * RADIUS_MULTIPLIER, getWorld().getMaxHeight());
}

@Override
Expand All @@ -49,19 +40,27 @@ public int getTier() {

@Override
public void setPrimaryEffect(PotionEffectType primary) {
this.primaryEffect = new PotionEffect(primary, 7, getTier(), true);
getBlockEntity().setPrimaryId(primary.getId());
getBlockEntity().setPrimaryEffectId(primary.getId());
}

@Override
public void setSecondaryEffect(PotionEffectType secondary) {
this.secondaryEffect = new PotionEffect(secondary, 7, getTier(), true);
getBlockEntity().setSecondaryId(secondary.getId());
getBlockEntity().setSecondaryEffectId(secondary.getId());
}

@Override
public PotionEffect getPrimaryEffect() {
return getBlockEntity().getEffect(BeaconEntity.BeaconEffectPriority.PRIMARY);
}

@Override
public PotionEffect getSecondaryEffect() {
return getBlockEntity().getEffect(BeaconEntity.BeaconEffectPriority.SECONDARY);
}

@Override
public BeaconInventory getInventory() {
throw new UnsupportedOperationException("Not supported yet.");
return new GlowBeaconInventory(this);
}

@Override
Expand Down
60 changes: 60 additions & 0 deletions src/main/java/net/glowstone/inventory/GlowBeaconInventory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package net.glowstone.inventory;

import com.google.common.collect.Sets;
import java.util.Set;
import org.bukkit.Material;
import org.bukkit.block.Beacon;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.BeaconInventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;

public class GlowBeaconInventory extends GlowInventory implements BeaconInventory {

private static final Set<Material> ALLOWED_MATERIALS = Sets.newHashSet(
Material.EMERALD, Material.DIAMOND,
Material.GOLD_INGOT, Material.IRON_INGOT
);
private static final int INPUT_SLOT = 0;

public GlowBeaconInventory(Beacon owner) {
super(owner, InventoryType.BEACON);

getSlot(INPUT_SLOT).setType(InventoryType.SlotType.CRAFTING);
}

public void setActiveEffects(int primaryId, int secondaryId) {
if (!ALLOWED_MATERIALS.contains(getItem().getType())) {
return;
}

PotionEffectType primaryType = PotionEffectType.getById(primaryId);
if (primaryType != null) {
((Beacon) getHolder()).setPrimaryEffect(primaryType);
}
PotionEffectType secondaryType = PotionEffectType.getById(secondaryId);
if (secondaryType != null) {
((Beacon) getHolder()).setSecondaryEffect(secondaryType);
}

getItem().add(-1);
}

@Override
public void setItem(ItemStack itemStack) {
setItem(INPUT_SLOT, itemStack);
}

@Override
public ItemStack getItem() {
return getItem(INPUT_SLOT);
}

@Override
public boolean itemPlaceAllowed(int slot, ItemStack stack) {
if (slot == INPUT_SLOT) {
return ALLOWED_MATERIALS.contains(stack.getType());
}
return super.itemPlaceAllowed(slot, stack);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import java.util.logging.Level;
import net.glowstone.GlowServer;
import net.glowstone.inventory.GlowAnvilInventory;
import net.glowstone.inventory.GlowBeaconInventory;
import net.glowstone.net.GlowBufUtils;
import net.glowstone.net.GlowSession;
import net.glowstone.net.message.play.game.PluginMessage;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
Expand Down Expand Up @@ -177,6 +179,18 @@ int entity (command block minecart)
((GlowAnvilInventory) session.getPlayer().getOpenInventory().getTopInventory())
.setRenameText(name);
break;
case "MC|Beacon": {
Player player = session.getPlayer();

if (player.getOpenInventory() == null || player.getOpenInventory().getType() != InventoryType.BEACON) {
break;
}

GlowBeaconInventory inventory = (GlowBeaconInventory) player.getOpenInventory().getTopInventory();
inventory.setActiveEffects(buf.readInt(), buf.readInt());

This comment has been minimized.

Copy link
@Pr0methean

Pr0methean Jun 23, 2020

Contributor

Doesn't this allow a maliciously modded client to activate an effect using the wrong item?

This comment has been minimized.

Copy link
@SHADOWDANCH

SHADOWDANCH Jun 23, 2020

Author Contributor

It checks for correct item here

This comment has been minimized.

Copy link
@Shevchik

Shevchik Jun 23, 2020

Contributor

That's item. We are talking about this code trusting the client sending correct active potion effects ids.


break;
}
default:
GlowServer.logger.info(session + " used unknown Minecraft channel: " + channel);
break;
Expand Down

0 comments on commit c4c59a7

Please sign in to comment.