import minigame api from wildwest reposiotry
This commit is contained in:
parent
2af29fe6bf
commit
dafb58155a
12 changed files with 469 additions and 0 deletions
|
|
@ -0,0 +1,20 @@
|
|||
package de.kentoj.scrow.bukkit.minigame;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface Minigame {
|
||||
|
||||
/**
|
||||
* Required participant count
|
||||
*/
|
||||
int getMinParticipants();
|
||||
|
||||
/**
|
||||
* Maximally allowed participant count
|
||||
*/
|
||||
int getMaxParticipants();
|
||||
|
||||
PlayerManager getPlayerManager();
|
||||
|
||||
CompletableFuture<Void> start();
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package de.kentoj.scrow.bukkit.minigame;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
public class PlayerManager {
|
||||
|
||||
private final Set<UUID> participants = new HashSet<>();
|
||||
private final Set<UUID> spectators = new HashSet<>();
|
||||
|
||||
public final Stream<Player> getParticipants() {
|
||||
return participants.stream().map(Bukkit::getPlayer);
|
||||
}
|
||||
|
||||
public final Stream<Player> getSpectators() {
|
||||
return spectators.stream().map(Bukkit::getPlayer);
|
||||
}
|
||||
|
||||
public int getParticipantCount() {
|
||||
return participants.size();
|
||||
}
|
||||
|
||||
public void addParticipant(Player player) {
|
||||
participants.add(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if the player was a participant
|
||||
*/
|
||||
public boolean removeParticipant(Player player) {
|
||||
return participants.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
public boolean isParticipant(Player player) {
|
||||
return participants.contains(player.getUniqueId());
|
||||
}
|
||||
|
||||
public void addSpectator(Player player) {
|
||||
spectators.add(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if the player was in a spectator
|
||||
*/
|
||||
public boolean removeSpectator(Player player) {
|
||||
return spectators.remove(player.getUniqueId());
|
||||
}
|
||||
|
||||
public Stream<Player> getAllPlayers() {
|
||||
return Stream.concat(getParticipants(), getSpectators());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package de.kentoj.scrow.bukkit.minigame.lobby;
|
||||
|
||||
import de.kentoj.scrow.bukkit.minigame.Minigame;
|
||||
import de.kentoj.scrow.bukkit.minigame.phase.AbstractPhase;
|
||||
import de.kentoj.scrow.bukkit.minigame.phase.LinearPhaseFlow;
|
||||
import de.kentoj.scrow.bukkit.minigame.phase.PhaseFlow;
|
||||
import de.kentoj.scrow.bukkit.minigame.phase.event.EventCanceller;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockExplodeEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
|
||||
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
||||
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
|
||||
import org.bukkit.event.player.*;
|
||||
|
||||
public class LobbyPhase extends AbstractPhase<Minigame> {
|
||||
|
||||
private final Minigame game;
|
||||
private final PhaseFlow phaseFlow;
|
||||
private final LinearPhaseFlow subPhaseFlow;
|
||||
|
||||
public LobbyPhase(Minigame game, PhaseFlow phaseFlow) {
|
||||
super(game, phaseFlow);
|
||||
this.game = game;
|
||||
this.phaseFlow = phaseFlow;
|
||||
subPhaseFlow = new LinearPhaseFlow("hunt.lobby");
|
||||
subPhaseFlow.addPhase(new WaitForMinimumPlayersPhase(game, subPhaseFlow));
|
||||
subPhaseFlow.addPhase(new WaitForMaximumPlayersPhase(game, subPhaseFlow));
|
||||
|
||||
addEventHandler(PlayerJoinEvent.class, this::onJoin);
|
||||
addEventHandler(PlayerQuitEvent.class, this::onQuit);
|
||||
|
||||
addEventHandler(HangingBreakByEntityEvent.class, new EventCanceller<>());
|
||||
addEventHandler(PlayerInteractAtEntityEvent.class, new EventCanceller<>());
|
||||
addEventHandler(PlayerInteractEntityEvent.class, new EventCanceller<>());
|
||||
addEventHandler(PlayerDropItemEvent.class, new EventCanceller<>());
|
||||
addEventHandler(BlockExplodeEvent.class, new EventCanceller<>());
|
||||
addEventHandler(EntityExplodeEvent.class, new EventCanceller<>());
|
||||
addEventHandler(EntityDamageEvent.class, new EventCanceller<>());
|
||||
addEventHandler(FoodLevelChangeEvent.class, new EventCanceller<>());
|
||||
addEventHandler(BlockBreakEvent.class, new EventCanceller<>());
|
||||
addEventHandler(BlockPlaceEvent.class, new EventCanceller<>());
|
||||
addEventHandler(EntityTargetLivingEntityEvent.class, new EventCanceller<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
subPhaseFlow.start().thenRun(phaseFlow::advancePhase);
|
||||
}
|
||||
|
||||
private void onJoin(PlayerJoinEvent ev) {
|
||||
game.getPlayerManager().addParticipant(ev.getPlayer());
|
||||
game.getPlayerManager().getAllPlayers().forEach(p ->
|
||||
p.sendMessage("§8[§a+§8] §7" + ev.getPlayer().getName()));
|
||||
}
|
||||
|
||||
private void onQuit(PlayerQuitEvent ev) {
|
||||
if (game.getPlayerManager().removeParticipant(ev.getPlayer()))
|
||||
game.getPlayerManager().getAllPlayers().forEach(p ->
|
||||
p.sendMessage("§8[§c-§8] §7" + ev.getPlayer().getName()));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package de.kentoj.scrow.bukkit.minigame.lobby;
|
||||
|
||||
import de.kentoj.scrow.bukkit.minigame.Minigame;
|
||||
import de.kentoj.scrow.bukkit.minigame.phase.AbstractPhase;
|
||||
import de.kentoj.scrow.bukkit.minigame.phase.PhaseFlow;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.title.Title;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class WaitForMaximumPlayersPhase extends AbstractPhase<Minigame> {
|
||||
|
||||
private final Minigame game;
|
||||
private final PhaseFlow phaseFlow;
|
||||
private int left;
|
||||
|
||||
WaitForMaximumPlayersPhase(Minigame game, PhaseFlow phaseFlow) {
|
||||
super(game, phaseFlow);
|
||||
this.game = game;
|
||||
this.phaseFlow = phaseFlow;
|
||||
addEventHandler(PlayerQuitEvent.class, this::onQuit);
|
||||
}
|
||||
|
||||
private void onQuit(PlayerQuitEvent ev) {
|
||||
var isEnough = game.getPlayerManager().getParticipantCount() >= game.getMinParticipants();
|
||||
if (!isEnough) phaseFlow.rewindPhase();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
this.left = 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTick(int tick) {
|
||||
if (tick % 20 == 0) {
|
||||
game.getPlayerManager().getAllPlayers().forEach(player -> player.showTitle(Title.title(
|
||||
Component.text("Game starting in " + left + " seconds"),
|
||||
Component.text("have fun"),
|
||||
Title.Times.times(Duration.ofMillis(50), Duration.ZERO, Duration.ofMillis(50))
|
||||
)));
|
||||
left--;
|
||||
if (left == 0) phaseFlow.advancePhase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package de.kentoj.scrow.bukkit.minigame.lobby;
|
||||
|
||||
import de.kentoj.scrow.bukkit.minigame.Minigame;
|
||||
import de.kentoj.scrow.bukkit.minigame.phase.AbstractPhase;
|
||||
import de.kentoj.scrow.bukkit.minigame.phase.PhaseFlow;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
public class WaitForMinimumPlayersPhase extends AbstractPhase<Minigame> {
|
||||
|
||||
private final Minigame game;
|
||||
private final PhaseFlow phaseFlow;
|
||||
|
||||
WaitForMinimumPlayersPhase(Minigame game, PhaseFlow phaseFlow) {
|
||||
super(game, phaseFlow);
|
||||
this.game = game;
|
||||
this.phaseFlow = phaseFlow;
|
||||
this.addEventHandler(PlayerJoinEvent.class, this::onJoin);
|
||||
}
|
||||
|
||||
private void onJoin(PlayerJoinEvent ev) {
|
||||
boolean isEnoughParticipants = game.getPlayerManager().getParticipantCount() >= game.getMinParticipants();
|
||||
if (isEnoughParticipants) phaseFlow.advancePhase();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package de.kentoj.scrow.bukkit.minigame.phase;
|
||||
|
||||
import com.google.errorprone.annotations.ForOverride;
|
||||
import de.kentoj.scrow.bukkit.ScrowAPI;
|
||||
import de.kentoj.scrow.bukkit.minigame.Minigame;
|
||||
import de.kentoj.scrow.bukkit.minigame.phase.event.EventHandler;
|
||||
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Slf4j
|
||||
public abstract class AbstractPhase<T extends Minigame> implements Phase {
|
||||
|
||||
private static final ScrowMessageStyle style = new ScrowMessageStyle("Game", NamedTextColor.RED);
|
||||
|
||||
private final List<PhaseEventHandler<?>> handlers = new ArrayList<>();
|
||||
private CompletableFuture<Void> future;
|
||||
|
||||
private final T game;
|
||||
private final PhaseFlow phaseFlow;
|
||||
|
||||
protected AbstractPhase(T game, PhaseFlow phaseFlow) {
|
||||
this.game = game;
|
||||
this.phaseFlow = phaseFlow;
|
||||
}
|
||||
|
||||
@ForOverride
|
||||
protected void onStart() {
|
||||
}
|
||||
|
||||
@ForOverride
|
||||
protected void onEnd() {
|
||||
}
|
||||
|
||||
@ForOverride
|
||||
protected void onTick(int tick) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void start() {
|
||||
if (isActive()) return;
|
||||
future = new CompletableFuture<>();
|
||||
future.thenRun(() -> handlers.forEach(PhaseEventHandler::disable));
|
||||
future.thenRun(() -> future = null);
|
||||
future.thenRun(this::onEnd);
|
||||
|
||||
handlers.forEach(PhaseEventHandler::enable);
|
||||
try {
|
||||
onStart();
|
||||
} catch (Throwable throwable) {
|
||||
log.error("Error while starting phase {}. Rewinding to previous phase...", getClass().getCanonicalName(), throwable);
|
||||
phaseFlow.rewindPhase();
|
||||
game.getPlayerManager().getAllPlayers().forEach(player ->
|
||||
player.sendMessage(style.err("Rewinding phase due to an error."))
|
||||
);
|
||||
}
|
||||
runTicker();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void end() {
|
||||
if (!isActive()) return;
|
||||
future.complete(null);
|
||||
}
|
||||
|
||||
public final <E extends Event> void addEventHandler(Class<E> eventClass, EventPriority priority, EventHandler<E> handler) {
|
||||
handlers.add(new PhaseEventHandler<>(eventClass, priority, handler::handle));
|
||||
}
|
||||
|
||||
public final <E extends Event> void addEventHandler(Class<E> eventClass, EventHandler<E> handler) {
|
||||
handlers.add(new PhaseEventHandler<>(eventClass, EventPriority.NORMAL, handler::handle));
|
||||
}
|
||||
|
||||
private boolean isActive() {
|
||||
return future != null;
|
||||
}
|
||||
|
||||
private void runTicker() {
|
||||
new BukkitRunnable() {
|
||||
int tick = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isActive()) {
|
||||
this.cancel();
|
||||
return;
|
||||
}
|
||||
onTick(tick++);
|
||||
}
|
||||
}.runTaskTimer(ScrowAPI.plugin, 1, 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package de.kentoj.scrow.bukkit.minigame.phase;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class LinearPhaseFlow implements PhaseFlow {
|
||||
|
||||
@Getter
|
||||
private final String name;
|
||||
|
||||
private final List<Phase> phases = new ArrayList<>();
|
||||
private CompletableFuture<Void> onEnd = null;
|
||||
private int curInd = -1;
|
||||
|
||||
public void addPhase(Phase phase) {
|
||||
this.phases.add(phase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void advancePhase() {
|
||||
Phase cur;
|
||||
|
||||
cur = getCurrent();
|
||||
if (cur != null) cur.end();
|
||||
|
||||
curInd++;
|
||||
cur = getCurrent();
|
||||
if (cur == null) {
|
||||
curInd--;
|
||||
onEnd.complete(null);
|
||||
return;
|
||||
}
|
||||
cur.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rewindPhase() {
|
||||
Phase cur;
|
||||
|
||||
cur = getCurrent();
|
||||
if (cur != null) cur.end();
|
||||
|
||||
curInd--;
|
||||
cur = getCurrent();
|
||||
if (cur == null) {
|
||||
curInd++;
|
||||
throw new IllegalStateException("no previous phase");
|
||||
}
|
||||
cur.start();
|
||||
}
|
||||
|
||||
private @Nullable Phase getCurrent() {
|
||||
if (curInd < 0 || curInd >= phases.size()) return null;
|
||||
return phases.get(curInd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> start() {
|
||||
Preconditions.checkState(onEnd == null, "attempting to start game that is already active");
|
||||
onEnd = new CompletableFuture<>();
|
||||
onEnd.thenRun(() -> onEnd = null);
|
||||
advancePhase();
|
||||
return onEnd;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package de.kentoj.scrow.bukkit.minigame.phase;
|
||||
|
||||
public interface Phase {
|
||||
|
||||
/**
|
||||
* will do nothing if the phase is already active.
|
||||
* Implementations shall advance the phase using {@link PhaseFlow#advancePhase()}.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* will do nothing if the phase is not active.
|
||||
* THIS DOES NOT ADVANCE TO THE NEXT PHASE. USE {@link PhaseFlow#advancePhase()} INSTEAD.
|
||||
**/
|
||||
void end();
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package de.kentoj.scrow.bukkit.minigame.phase;
|
||||
|
||||
import de.kentoj.scrow.bukkit.ScrowAPI;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
class PhaseEventHandler<E extends Event> implements Listener {
|
||||
private final Class<E> eventClass;
|
||||
private final EventPriority priority;
|
||||
private final Consumer<E> handler;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void enable() {
|
||||
Bukkit.getPluginManager().registerEvent(eventClass, this, priority,
|
||||
(__, ev) -> handler.accept((E) ev), ScrowAPI.plugin);
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package de.kentoj.scrow.bukkit.minigame.phase;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public interface PhaseFlow {
|
||||
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* end the current phase, go to the next phase and activate it
|
||||
*/
|
||||
void advancePhase();
|
||||
|
||||
/**
|
||||
* end the current phase, go to the previous phase and activate it
|
||||
*/
|
||||
void rewindPhase();
|
||||
|
||||
/**
|
||||
* CompletableFuture is completed when the game finishes
|
||||
*/
|
||||
CompletableFuture<Void> start();
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package de.kentoj.scrow.bukkit.minigame.phase.event;
|
||||
|
||||
import org.bukkit.event.Cancellable;
|
||||
|
||||
public class EventCanceller<T extends Cancellable> implements EventHandler<T> {
|
||||
@Override
|
||||
public void handle(Cancellable ev) {
|
||||
ev.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package de.kentoj.scrow.bukkit.minigame.phase.event;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface EventHandler<T> {
|
||||
|
||||
void handle(T ev);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue