This commit is contained in:
kento2 2026-07-30 14:24:00 +02:00
parent ca60c10263
commit 6458486810
16 changed files with 234 additions and 117 deletions

View file

@ -1,6 +1,7 @@
package de.kentoj.scrow.bukkit; package de.kentoj.scrow.bukkit;
import de.kentoj.kencommandapi.CommandAPI; import de.kentoj.kencommandapi.CommandAPI;
import de.kentoj.scrow.bukkit.minigame.MinigameManager;
import de.kentoj.scrowlib.friends.FriendRequestService; import de.kentoj.scrowlib.friends.FriendRequestService;
import de.kentoj.scrowlib.friends.FriendshipService; import de.kentoj.scrowlib.friends.FriendshipService;
import de.kentoj.scrowlib.economy.EconomyService; import de.kentoj.scrowlib.economy.EconomyService;
@ -15,7 +16,6 @@ import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.checkerframework.checker.signature.qual.InternalForm;
import org.jdbi.v3.core.Jdbi; import org.jdbi.v3.core.Jdbi;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -48,6 +48,8 @@ public class ScrowAPI {
private static CommandAPI commandApi; private static CommandAPI commandApi;
@Getter @Getter
private static PlayerPrefixProvider playerPrefixProvider; private static PlayerPrefixProvider playerPrefixProvider;
@Getter
private static final MinigameManager minigameManager = new MinigameManager();
private static NetworkPlayerFactory playerFactory; private static NetworkPlayerFactory playerFactory;
public static NetworkPlayer getPlayer(OfflinePlayer player) { public static NetworkPlayer getPlayer(OfflinePlayer player) {

View file

@ -0,0 +1,6 @@
package de.kentoj.scrow.bukkit.gameserver;
public interface GameServer {
void destroy();
}

View file

@ -0,0 +1,8 @@
package de.kentoj.scrow.bukkit.gameserver;
public interface GameServerPool {
GameServer prepareServer(String gameName);
void returnServer(String name);
}

View file

@ -0,0 +1,24 @@
package de.kentoj.scrow.bukkit.minigame;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class MinigameManager {
private final Map<UUID, Minigame> playerToMinigameMap = new HashMap<>();
public @Nullable Minigame getMinigameByPlayer(OfflinePlayer player) {
return playerToMinigameMap.get(player.getUniqueId());
}
public void setMinigame(OfflinePlayer player, @Nullable Minigame minigame) {
if (minigame == null)
playerToMinigameMap.remove(player.getUniqueId());
else
playerToMinigameMap.put(player.getUniqueId(), minigame);
}
}

View file

@ -1,5 +1,7 @@
package de.kentoj.scrow.bukkit.minigame; package de.kentoj.scrow.bukkit.minigame;
import de.kentoj.scrow.bukkit.ScrowAPI;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -8,17 +10,20 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Stream; import java.util.stream.Stream;
@RequiredArgsConstructor
public class PlayerManager { public final class PlayerManager {
private final Set<UUID> participants = new HashSet<>(); private final Set<UUID> participants = new HashSet<>();
private final Set<UUID> spectators = new HashSet<>(); private final Set<UUID> spectators = new HashSet<>();
public final Stream<Player> getParticipants() { private final MinigameManager minigameManager = ScrowAPI.getMinigameManager();
private final Minigame minigame;
public Stream<Player> getParticipants() {
return participants.stream().map(Bukkit::getPlayer); return participants.stream().map(Bukkit::getPlayer);
} }
public final Stream<Player> getSpectators() { public Stream<Player> getSpectators() {
return spectators.stream().map(Bukkit::getPlayer); return spectators.stream().map(Bukkit::getPlayer);
} }
@ -28,12 +33,14 @@ public class PlayerManager {
public void addParticipant(Player player) { public void addParticipant(Player player) {
participants.add(player.getUniqueId()); participants.add(player.getUniqueId());
minigameManager.setMinigame(player, minigame);
} }
/** /**
* @return if the player was a participant * @return if the player was a participant
*/ */
public boolean removeParticipant(Player player) { public boolean removeParticipant(Player player) {
minigameManager.setMinigame(player, null);
return participants.remove(player.getUniqueId()); return participants.remove(player.getUniqueId());
} }

View file

@ -0,0 +1,46 @@
package de.kentoj.scrow.bukkit.minigame.lobby;
import de.kentoj.scrow.bukkit.gameserver.GameServer;
import de.kentoj.scrow.bukkit.minigame.Minigame;
import de.kentoj.scrow.bukkit.minigame.PlayerManager;
import de.kentoj.scrowlib.convention.ScrowMessageStyle;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import java.util.concurrent.CompletableFuture;
@RequiredArgsConstructor
public class LobbyMinigame implements Minigame {
@Getter
private final String gameName;
@Getter
private final ScrowMessageStyle messageStyle;
@Getter
@Setter
private GameServer gameServer = null;
@Getter
@Setter
private CompletableFuture<Void> destroyGameServer;
@Override
public int getMinParticipants() {
return 0;
}
@Override
public int getMaxParticipants() {
return 0;
}
@Override
public PlayerManager getPlayerManager() {
return null;
}
@Override
public CompletableFuture<Void> start() {
return null;
}
}

View file

@ -1,9 +1,8 @@
package de.kentoj.scrow.bukkit.minigame.lobby; package de.kentoj.scrow.bukkit.minigame.lobby;
import de.kentoj.scrow.bukkit.minigame.LinearPhaseFlow; import de.kentoj.scrow.bukkit.minigame.phaseflow.LinearPhaseFlow;
import de.kentoj.scrow.bukkit.minigame.Minigame;
import de.kentoj.scrow.bukkit.minigame.phase.CompositePhase; import de.kentoj.scrow.bukkit.minigame.phase.CompositePhase;
import de.kentoj.scrow.bukkit.minigame.phase.PhaseContext; import de.kentoj.scrow.bukkit.minigame.phaseflow.PhaseContext;
import lombok.Getter; import lombok.Getter;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBreakEvent;
@ -16,16 +15,16 @@ import org.bukkit.event.entity.FoodLevelChangeEvent;
import org.bukkit.event.hanging.HangingBreakByEntityEvent; import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.player.*; import org.bukkit.event.player.*;
public class LobbyPhase<T extends Minigame> extends CompositePhase<T> { public class LobbyPhase extends CompositePhase<LobbyMinigame> {
@Getter @Getter
private final LinearPhaseFlow subPhaseFlow = new LinearPhaseFlow("lobby"); private final LinearPhaseFlow subPhaseFlow = new LinearPhaseFlow("lobby");
public LobbyPhase(PhaseContext<T> ctx) { public LobbyPhase(PhaseContext<LobbyMinigame> ctx) {
super(ctx); super(ctx);
var subCtx = new PhaseContext<>(ctx.game(), subPhaseFlow); PhaseContext<LobbyMinigame> subCtx = new PhaseContext<>(ctx.game(), subPhaseFlow);
subPhaseFlow.add(new WaitForMinimumPlayersPhase<>(subCtx)); subPhaseFlow.add(new WaitForMinimumPlayersPhase<>(subCtx));
subPhaseFlow.add(new WaitForMaximumPlayersPhase<>(subCtx)); subPhaseFlow.add(new WaitForMaximumPlayersPhase(subCtx));
} }
@Override @Override

View file

@ -1,27 +1,51 @@
package de.kentoj.scrow.bukkit.minigame.lobby; package de.kentoj.scrow.bukkit.minigame.lobby;
import de.kentoj.scrow.bukkit.minigame.Minigame; import de.kentoj.scrow.bukkit.gameserver.GameServer;
import de.kentoj.scrow.bukkit.minigame.phase.AbstractPhase; import de.kentoj.scrow.bukkit.minigame.phase.Phase;
import de.kentoj.scrow.bukkit.minigame.phase.PhaseContext; import de.kentoj.scrow.bukkit.minigame.phaseflow.PhaseContext;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
public class WaitForMaximumPlayersPhase<T extends Minigame> extends AbstractPhase<T> { import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class WaitForMaximumPlayersPhase extends Phase<LobbyMinigame> {
private int secsLeft; private int secsLeft;
private CompletableFuture<GameServer> deployedServer;
public WaitForMaximumPlayersPhase(PhaseContext<T> ctx) { public WaitForMaximumPlayersPhase(PhaseContext<LobbyMinigame> ctx) {
super(ctx); super(ctx);
var delayedExecutor = CompletableFuture.delayedExecutor(3, TimeUnit.MINUTES);
ctx.game().setDestroyGameServer(CompletableFuture.runAsync(() -> {
synchronized (ctx.game().getGameServer()) {
ctx.game().getGameServer().destroy();
}
}, delayedExecutor));
} }
@Override @Override
public void onStart() { public void onStart() {
this.secsLeft = 5; this.secsLeft = 5;
ctx.listeners().on(PlayerQuitEvent.class, this::onQuit); ctx.listeners().on(PlayerQuitEvent.class, this::onQuit);
/*
this.deployedServer=Tasks.supplyAsync(() ->
gameServers.deployServer(ctx.game().getGameName()))
.mapSync(server -> {
ctx.game().setGameServer(server);
return server;
}).toFuture()
ctx.game().setGameServer();
*/
} }
private void onQuit(PlayerQuitEvent ev) { private void onQuit(PlayerQuitEvent ev) {
var isEnough = ctx.players().getParticipantCount() >= ctx.game().getMinParticipants(); var isEnough = ctx.players().getParticipantCount() >= ctx.game().getMinParticipants();
if (!isEnough) ctx.phases().rewindPhase(); if (!isEnough) {
//deployedServer
ctx.phases().rewindPhase();
}
} }
@Override @Override
@ -31,7 +55,7 @@ public class WaitForMaximumPlayersPhase<T extends Minigame> extends AbstractPhas
player.sendTitle("Game starting in " + secsLeft + " seconds", "have fun", 5, 20, 10) player.sendTitle("Game starting in " + secsLeft + " seconds", "have fun", 5, 20, 10)
); );
secsLeft--; secsLeft--;
if (secsLeft == 0) super.advancePhase(); if (secsLeft == 0) ctx.advancePhase();
} }
} }

View file

@ -1,11 +1,11 @@
package de.kentoj.scrow.bukkit.minigame.lobby; package de.kentoj.scrow.bukkit.minigame.lobby;
import de.kentoj.scrow.bukkit.minigame.Minigame; import de.kentoj.scrow.bukkit.minigame.Minigame;
import de.kentoj.scrow.bukkit.minigame.phase.AbstractPhase; import de.kentoj.scrow.bukkit.minigame.phase.Phase;
import de.kentoj.scrow.bukkit.minigame.phase.PhaseContext; import de.kentoj.scrow.bukkit.minigame.phaseflow.PhaseContext;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
public class WaitForMinimumPlayersPhase<T extends Minigame> extends AbstractPhase<T> { public class WaitForMinimumPlayersPhase<T extends Minigame> extends Phase<T> {
public WaitForMinimumPlayersPhase(PhaseContext<T> ctx) { public WaitForMinimumPlayersPhase(PhaseContext<T> ctx) {
super(ctx); super(ctx);

View file

@ -1,78 +0,0 @@
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.PhaseFlow;
import lombok.RequiredArgsConstructor;
import org.bukkit.scheduler.BukkitRunnable;
@RequiredArgsConstructor
public abstract class AbstractPhase<T extends Minigame> implements Phase {
protected final PhaseContext<T> ctx;
private boolean isActive = false;
/**
* Shall call {@link PhaseFlow#advancePhase()}
*/
protected abstract void onStart();
/**
* Called together with {@link AbstractPhase#onEnd()} but only called when the phase was cancelled
* due to an error
*/
protected abstract void onCancel();
@ForOverride
protected void onEnd() {
}
@ForOverride
protected void onTick(int tick) {
}
@Override
public void cancel() {
onCancel();
end();
}
public final void start() {
if (isActive) return;
isActive = true;
onStart();
runTicker();
}
public final void end() {
if (!isActive) return;
ctx.listeners().unregisterAll();
onEnd();
isActive = false;
}
/**
* end the current phase and advance to the next.
* same as {@link PhaseFlow#advancePhase()}.
*/
public final void advancePhase() {
ctx.phases().advancePhase();
}
private void runTicker() {
new BukkitRunnable() {
int tick = 0;
@Override
public void run() {
if (!isActive) {
this.cancel();
return;
}
onTick(tick++);
}
}.runTaskTimer(ScrowAPI.plugin, 1, 1);
}
}

View file

@ -1,9 +1,9 @@
package de.kentoj.scrow.bukkit.minigame.phase; package de.kentoj.scrow.bukkit.minigame.phase;
import de.kentoj.scrow.bukkit.minigame.Minigame; import de.kentoj.scrow.bukkit.minigame.Minigame;
import de.kentoj.scrow.bukkit.minigame.PhaseFlow; import de.kentoj.scrow.bukkit.minigame.phaseflow.PhaseContext;
public abstract class CompositePhase<T extends Minigame> extends AbstractPhase<T> { public abstract class CompositePhase<T extends Minigame> extends Phase<T> {
public CompositePhase(PhaseContext<T> ctx) { public CompositePhase(PhaseContext<T> ctx) {
super(ctx); super(ctx);

View file

@ -0,0 +1,10 @@
package de.kentoj.scrow.bukkit.minigame.phase;
public interface IPhase {
void start();
void end();
void cancel();
}

View file

@ -1,10 +1,78 @@
package de.kentoj.scrow.bukkit.minigame.phase; package de.kentoj.scrow.bukkit.minigame.phase;
public interface 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.phaseflow.PhaseContext;
import lombok.RequiredArgsConstructor;
import org.bukkit.scheduler.BukkitRunnable;
void start(); @RequiredArgsConstructor
public abstract class Phase<T extends Minigame> implements IPhase {
void end(); protected final PhaseContext<T> ctx;
void cancel(); private boolean isActive = false;
/**
* Shall call {@link PhaseFlow#advancePhase()}
*/
protected abstract void onStart();
/**
* Called together with {@link Phase#onEnd()} but only called when the phase was cancelled
* due to an error
*/
protected abstract void onCancel();
@ForOverride
protected void onEnd() {
}
@ForOverride
protected void onTick(int tick) {
}
@Override
public void cancel() {
end();
onCancel();
}
public final void start() {
if (isActive) return;
isActive = true;
onStart();
runTicker();
}
public final void end() {
if (!isActive) return;
ctx.listeners().unregisterAll();
onEnd();
isActive = false;
}
/**
* end the current phase and advance to the next.
* same as {@link PhaseFlow#advancePhase()}.
*/
public final void advancePhase() {
ctx.phases().advancePhase();
}
private void runTicker() {
new BukkitRunnable() {
int tick = 0;
@Override
public void run() {
if (!isActive) {
this.cancel();
return;
}
onTick(tick++);
}
}.runTaskTimer(ScrowAPI.plugin, 1, 1);
}
} }

View file

@ -1,4 +1,4 @@
package de.kentoj.scrow.bukkit.minigame; package de.kentoj.scrow.bukkit.minigame.phase;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;

View file

@ -1,7 +1,8 @@
package de.kentoj.scrow.bukkit.minigame; package de.kentoj.scrow.bukkit.minigame.phaseflow;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import de.kentoj.scrow.bukkit.minigame.phase.Phase; import de.kentoj.scrow.bukkit.minigame.phase.IPhase;
import de.kentoj.scrow.bukkit.minigame.phase.PhaseFlow;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -18,17 +19,17 @@ public class LinearPhaseFlow implements PhaseFlow {
@Getter @Getter
private final String name; private final String name;
private final List<Phase> phases = new ArrayList<>(); private final List<IPhase> phases = new ArrayList<>();
private CompletableFuture<Void> onEnd = null; private CompletableFuture<Void> onEnd = null;
private int curInd = -1; private int curInd = -1;
public void add(Phase phase) { public void add(IPhase phase) {
this.phases.add(phase); this.phases.add(phase);
} }
@Override @Override
public void advancePhase() { public void advancePhase() {
Phase cur; IPhase cur;
cur = current(); cur = current();
if (cur != null) { if (cur != null) {
cur.cancel(); cur.cancel();
@ -82,11 +83,11 @@ public class LinearPhaseFlow implements PhaseFlow {
return onEnd; return onEnd;
} }
private void startPhase(Phase phase) { private void startPhase(IPhase phase) {
this.startPhase(phase, true); this.startPhase(phase, true);
} }
private void startPhase(Phase phase, boolean recoverOnError) { private void startPhase(IPhase phase, boolean recoverOnError) {
if (!recoverOnError) { if (!recoverOnError) {
phase.start(); phase.start();
return; return;
@ -101,7 +102,7 @@ public class LinearPhaseFlow implements PhaseFlow {
} }
} }
private @Nullable Phase current() { private @Nullable IPhase current() {
if (curInd < 0 || curInd >= phases.size()) return null; if (curInd < 0 || curInd >= phases.size()) return null;
return phases.get(curInd); return phases.get(curInd);
} }

View file

@ -1,9 +1,9 @@
package de.kentoj.scrow.bukkit.minigame.phase; package de.kentoj.scrow.bukkit.minigame.phaseflow;
import de.kentoj.kencommandapi.api.platform.MessageStyle; import de.kentoj.kencommandapi.api.platform.MessageStyle;
import de.kentoj.scrow.bukkit.minigame.Minigame; import de.kentoj.scrow.bukkit.minigame.Minigame;
import de.kentoj.scrow.bukkit.minigame.PhaseFlow;
import de.kentoj.scrow.bukkit.minigame.PlayerManager; import de.kentoj.scrow.bukkit.minigame.PlayerManager;
import de.kentoj.scrow.bukkit.minigame.phase.PhaseFlow;
import de.kentoj.scrow.bukkit.minigame.phase.event.PhaseListeners; import de.kentoj.scrow.bukkit.minigame.phase.event.PhaseListeners;
import de.kentoj.scrow.bukkit.minigame.phase.event.PhaseListenersImpl; import de.kentoj.scrow.bukkit.minigame.phase.event.PhaseListenersImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;