Add PotionEffectManager

This commit is contained in:
ThatCreeper 2020-12-30 17:12:03 -06:00
parent d4110632a4
commit 50b44e46e0
11 changed files with 390 additions and 13 deletions

View File

@ -33,6 +33,7 @@ import net.minestom.server.network.packet.server.play.UpdateViewDistancePacket;
import net.minestom.server.particle.Particle;
import net.minestom.server.ping.ResponseDataConsumer;
import net.minestom.server.potion.PotionEffect;
import net.minestom.server.potion.PotionEffectManager;
import net.minestom.server.potion.PotionType;
import net.minestom.server.recipe.RecipeManager;
import net.minestom.server.registry.ResourceGatherer;
@ -116,6 +117,7 @@ public final class MinecraftServer {
private static DimensionTypeManager dimensionTypeManager;
private static BiomeManager biomeManager;
private static AdvancementManager advancementManager;
private static PotionEffectManager potionEffectManager;
private static ExtensionManager extensionManager;
@ -177,6 +179,7 @@ public final class MinecraftServer {
dimensionTypeManager = new DimensionTypeManager();
biomeManager = new BiomeManager();
advancementManager = new AdvancementManager();
potionEffectManager = new PotionEffectManager();
updateManager = new UpdateManager();
@ -621,6 +624,16 @@ public final class MinecraftServer {
return advancementManager;
}
/**
* Gets the manager handling potions.
*
* @return the potion effect manager
*/
public static PotionEffectManager getPotionEffectManager() {
checkInitStatus(potionEffectManager);
return potionEffectManager;
}
/**
* Get the manager handling {@link Extension}.
*

View File

@ -14,6 +14,7 @@ import net.minestom.server.network.packet.client.play.ClientPlayerDiggingPacket;
import net.minestom.server.network.packet.server.play.AcknowledgePlayerDiggingPacket;
import net.minestom.server.network.packet.server.play.EntityEffectPacket;
import net.minestom.server.network.packet.server.play.RemoveEntityEffectPacket;
import net.minestom.server.potion.Potion;
import net.minestom.server.potion.PotionEffect;
import net.minestom.server.utils.BlockPosition;
@ -189,10 +190,14 @@ public class PlayerDiggingListener {
EntityEffectPacket entityEffectPacket = new EntityEffectPacket();
entityEffectPacket.entityId = player.getEntityId();
entityEffectPacket.effect = PotionEffect.MINING_FATIGUE;
entityEffectPacket.amplifier = -1;
entityEffectPacket.duration = 0;
entityEffectPacket.flags = 0;
entityEffectPacket.potion = new Potion(
PotionEffect.MINING_FATIGUE,
(byte) -1,
0,
false,
false,
false
);
player.getPlayerConnection().sendPacket(entityEffectPacket);
}

View File

@ -2,25 +2,22 @@ package net.minestom.server.network.packet.server.play;
import net.minestom.server.network.packet.server.ServerPacket;
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
import net.minestom.server.potion.PotionEffect;
import net.minestom.server.potion.Potion;
import net.minestom.server.utils.binary.BinaryWriter;
import org.jetbrains.annotations.NotNull;
public class EntityEffectPacket implements ServerPacket {
public int entityId;
public PotionEffect effect;
public byte amplifier;
public int duration;
public byte flags;
public Potion potion;
@Override
public void write(@NotNull BinaryWriter writer) {
writer.writeVarInt(entityId);
writer.writeByte((byte) effect.getId());
writer.writeByte(amplifier);
writer.writeVarInt(duration);
writer.writeByte(flags);
writer.writeByte((byte) potion.effect.getId());
writer.writeByte(potion.amplifier);
writer.writeVarInt(potion.duration);
writer.writeByte(potion.getFlags());
}
@Override

View File

@ -0,0 +1,58 @@
package net.minestom.server.potion;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.server.play.EntityEffectPacket;
import net.minestom.server.network.packet.server.play.RemoveEntityEffectPacket;
import org.jetbrains.annotations.NotNull;
public class Potion {
public PotionEffect effect;
public byte amplifier;
public int duration;
public boolean ambient;
public boolean particles;
public boolean icon;
public Potion(PotionEffect effect, byte amplifier, int duration) {
this(effect, amplifier, duration, true, true, false);
}
public Potion(PotionEffect effect, byte amplifier, int duration, boolean particles) {
this(effect, amplifier, duration, particles, true, false);
}
public Potion(PotionEffect effect, byte amplifier, int duration, boolean particles, boolean icon) {
this(effect, amplifier, duration, particles, icon, false);
}
public Potion(PotionEffect effect, byte amplifier, int duration, boolean particles, boolean icon, boolean ambient) {
this.effect = effect;
this.amplifier = amplifier;
this.duration = duration;
this.particles = particles;
this.icon = icon;
this.ambient = ambient;
}
public byte getFlags() {
byte computed = 0x00;
if (ambient) computed = (byte)(computed | 0x01);
if (particles) computed = (byte)(computed | 0x02);
if (icon) computed = (byte)(computed | 0x04);
return computed;
}
public void sendAddPacket(@NotNull Player player) {
EntityEffectPacket eep = new EntityEffectPacket();
eep.entityId = player.getEntityId();
eep.potion = this;
player.sendPacketToViewersAndSelf(eep);
}
public void sendRemovePacket(@NotNull Player player) {
RemoveEntityEffectPacket reep = new RemoveEntityEffectPacket();
reep.entityId = player.getEntityId();
reep.effect = effect;
player.sendPacketToViewersAndSelf(reep);
}
}

View File

@ -0,0 +1,185 @@
package net.minestom.server.potion;
import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player;
import net.minestom.server.event.GlobalEventHandler;
import net.minestom.server.event.player.PlayerDisconnectEvent;
import net.minestom.server.event.player.PlayerSpawnEvent;
import net.minestom.server.timer.SchedulerManager;
import net.minestom.server.utils.time.TimeUnit;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
/**
* Manages active potion effects on players
*/
public class PotionEffectManager {
private HashMap<UUID, ArrayList<PotionTask>> playerEffects;
private boolean persistEffects = false;
/**
* Creates a new PotionEffectManager
*/
public PotionEffectManager() {
playerEffects = new HashMap<>();
addEventHandlers();
}
/**
* Returns the active effects of a player.
*
* @param player The player to get the effects of.
* @return Null if no active effects, otherwise the active effects.
*/
public @Nullable List<PotionTask> getActiveEffects(@Nullable Player player) {
if (player == null) return null;
return playerEffects.get(player.getUuid());
}
/**
* Returns a {@link PotionTask} on a player of a type {@link PotionEffect}
*
* @param player The player with the effect.
* @param potionEffect The type of potion to look for.
* @return Null if the player does not have the effect, otherwise the effect.
*/
public @Nullable PotionTask getPotionTask(@Nullable Player player, @Nullable PotionEffect potionEffect) {
if (player == null) return null;
if (potionEffect == null) return null;
ArrayList<PotionTask> potionTasks = playerEffects.get(player.getUuid());
if (potionTasks == null) return null;
for (PotionTask potionTask : potionTasks) {
if (potionTask.getPotion().effect == potionEffect) {
return potionTask;
}
}
return null;
}
/**
* Returns if the player has an effect of specified type.
*
* @param player The player to check.
* @param potionEffect The type of potion to check for.
* @return If the player has a potion effect with the specified type.
*/
public boolean hasPotionEffect(@Nullable Player player, @Nullable PotionEffect potionEffect) {
return getPotionTask(player, potionEffect) != null;
}
/**
* Removes the {@link PotionTask}.
*
* @param task The {@link PotionTask} to remove.
*/
public void removeEffect(@NotNull PotionTask task) {
task.getValue().task.cancel();
for (UUID uuid : playerEffects.keySet()) {
if (playerEffects.get(uuid).contains(task)) {
playerEffects.get(uuid).remove(task);
Player player = MinecraftServer.getConnectionManager().getPlayer(uuid);
if (player != null && player.isOnline()) {
task.getPotion().sendRemovePacket(player);
}
return;
}
}
}
/**
* If the player has a potion of type effect, remove it.
*
* @param player The player to remove from.
* @param effect The {@link PotionEffect} to remove.
*/
public void removeEffect(@NotNull Player player, @NotNull PotionEffect effect) {
PotionTask task = null;
ArrayList<PotionTask> potionTasks = playerEffects.get(player.getUuid());
if (potionTasks == null) return;
for (PotionTask potionTask : potionTasks) {
if (potionTask.getPotion().effect == effect) {
task = potionTask;
break;
}
}
if (task == null) return;
removeEffect(task);
}
/**
* Clears a player's effects.
*
* @param player The player to clear.
*/
public void clearEffects(@Nullable Player player) {
if (player == null) return;
ArrayList<PotionTask> potionTasks = playerEffects.get(player.getUuid());
if (potionTasks == null) return;
for (PotionTask potionTask : potionTasks) {
potionTask.getValue().task.cancel();
if (player.isOnline()) {
potionTask.getPotion().sendRemovePacket(player);
}
}
potionTasks.clear();
playerEffects.remove(player.getUuid());
}
/**
* Gives a player a potion effect.
*
* @param player The player to give to.
* @param potion The {@link Potion} to give.
*/
public void addPotion(@Nullable Player player, @Nullable Potion potion) {
if (player == null) return;
if (potion == null) return;
removeEffect(player, potion.effect);
ArrayList<PotionTask> potionTasks = playerEffects.computeIfAbsent(player.getUuid(), k -> new ArrayList<>());
SchedulerManager schedulerManager = MinecraftServer.getSchedulerManager();
PotionTimeTask ptt = new PotionTimeTask(MinecraftServer.getSchedulerManager(),
TimeUnit.TICK.toMilliseconds(potion.duration));
potionTasks.add(new PotionTask(potion, ptt));
potion.sendAddPacket(player);
}
// NOT IMPLEMENTED
// public void setPersistEffects(Boolean persistEffects) {
// this.persistEffects = persistEffects;
// }
//
// public boolean getPersistEffects() {
// return persistEffects;
// }
/**
* Adds a {@link PlayerSpawnEvent} and {@link PlayerDisconnectEvent} handler to pause and resume potion effects.
*/
private void addEventHandlers() {
GlobalEventHandler globalEventHandler = MinecraftServer.getGlobalEventHandler();
globalEventHandler.addEventCallback(PlayerSpawnEvent.class, playerSpawnEvent -> {
if (persistEffects) {
// TODO: Implement persist
}
});
globalEventHandler.addEventCallback(PlayerDisconnectEvent.class, playerDisconnectEvent -> {
if (persistEffects) {
// TODO: Implement persist
} else {
clearEffects(playerDisconnectEvent.getPlayer());
}
});
}
}

View File

@ -0,0 +1,25 @@
package net.minestom.server.potion;
import javafx.util.Pair;
import net.minestom.server.MinecraftServer;
public class PotionTask extends Pair<Potion, PotionTimeTask> {
/**
* Creates a new PotionTask
*
* @param potion The {@link Potion}
* @param task The {@link PotionTimeTask}
*/
public PotionTask(Potion potion, PotionTimeTask task) {
super(potion, task);
task.potionTask = this;
}
public Potion getPotion() {
return this.getKey();
}
public void removeEffect() {
MinecraftServer.getPotionEffectManager().removeEffect(this);
}
}

View File

@ -0,0 +1,27 @@
package net.minestom.server.potion;
import net.minestom.server.timer.SchedulerManager;
import net.minestom.server.timer.Task;
import net.minestom.server.utils.time.TimeUnit;
import org.jetbrains.annotations.NotNull;
public class PotionTimeTask {
public Long remainingTime;
public Task task;
public PotionTask potionTask;
/**
* Creates a task.
*
* @param schedulerManager The manager for the task
* @param delay The time to delay
*/
public PotionTimeTask(@NotNull SchedulerManager schedulerManager, long delay) {
Runnable runTask = () -> {
potionTask.removeEffect();
};
task = schedulerManager.buildTask(runTask).delay(delay, TimeUnit.MILLISECOND).schedule();
this.remainingTime = delay;
}
}

View File

@ -0,0 +1,12 @@
package net.minestom.server.potion;
public class PotionTimeTaskHolder {
/**
* Only updates when task is paused
*/
public Long timeRemaining;
public PotionTimeTaskHolder() {
// TODO: Implement persist
}
}

View File

@ -39,6 +39,7 @@ public class Main {
commandManager.register(new ShutdownCommand());
commandManager.register(new TeleportCommand());
commandManager.register(new PlayersCommand());
commandManager.register(new PotionCommand());
commandManager.setUnknownCommandCallback((sender, command) -> sender.sendMessage("unknown command"));

View File

@ -29,6 +29,7 @@ import net.minestom.server.item.Material;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.packet.server.play.PlayerListHeaderAndFooterPacket;
import net.minestom.server.ping.ResponseDataConsumer;
import net.minestom.server.timer.TaskBuilder;
import net.minestom.server.utils.PacketUtils;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.Vector;

View File

@ -0,0 +1,53 @@
package demo.commands;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.Arguments;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.arguments.Argument;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.entity.Player;
import net.minestom.server.potion.Potion;
import net.minestom.server.potion.PotionEffect;
import net.minestom.server.potion.PotionTask;
import java.util.List;
public class PotionCommand extends Command {
public PotionCommand() {
super("potion");
setCondition(this::condition);
setDefaultExecutor(((sender, args) -> {
sender.sendMessage("Usage: /potion [type] [duration (seconds)]");
}));
Argument potionArg = ArgumentType.Potion("potion");
Argument durationArg = ArgumentType.Integer("duration");
addSyntax(this::onPotionCommand, potionArg, durationArg);
}
private boolean condition(CommandSender sender, String commandString) {
if (!sender.isPlayer()) {
sender.sendMessage("The command is only available for players");
return false;
}
return true;
}
private void onPotionCommand(CommandSender sender, Arguments args) {
final Player player = (Player) sender;
final PotionEffect potion = args.getPotionEffect("potion");
final int duration = args.getInteger("duration");
MinecraftServer.getPotionEffectManager().addPotion(player, new Potion(
potion,
(byte) 1,
duration * 20
));
}
}