diff --git a/src/main/java/xyz/nucleoid/plasmid/api/game/GameTexts.java b/src/main/java/xyz/nucleoid/plasmid/api/game/GameTexts.java
index cd2e7813..84ebf563 100644
--- a/src/main/java/xyz/nucleoid/plasmid/api/game/GameTexts.java
+++ b/src/main/java/xyz/nucleoid/plasmid/api/game/GameTexts.java
@@ -156,6 +156,14 @@ public static MutableText inOtherGame() {
public static MutableText notAllowed() {
return Text.translatable("text.plasmid.join_result.not_allowed");
}
+
+ public static MutableText spectatorsOnly() {
+ return Text.translatable("text.plasmid.join_result.spectators_only");
+ }
+
+ public static MutableText participantsOnly() {
+ return Text.translatable("text.plasmid.join_result.participants_only");
+ }
}
public static final class Kick {
diff --git a/src/main/java/xyz/nucleoid/plasmid/api/game/player/JoinOffer.java b/src/main/java/xyz/nucleoid/plasmid/api/game/player/JoinOffer.java
index 96eee49f..b7ea118d 100644
--- a/src/main/java/xyz/nucleoid/plasmid/api/game/player/JoinOffer.java
+++ b/src/main/java/xyz/nucleoid/plasmid/api/game/player/JoinOffer.java
@@ -8,6 +8,7 @@
import java.util.Set;
import java.util.UUID;
+import java.util.function.Function;
import java.util.stream.Collectors;
/**
@@ -83,4 +84,54 @@ default JoinOfferResult.Accept accept() {
default JoinOfferResult pass() {
return JoinOfferResult.PASS;
}
+
+ /**
+ * Returns an offer result that accepts this offer only if the intent is to spectate.
+ *
+ * This function does not do anything on its own, but its result must be returned within a
+ * {@link GamePlayerEvents#OFFER} listener.
+ *
+ * @return an "accept" offer result for spectators and "reject" offer result for participants
+ */
+ default JoinOfferResult acceptSpectators() {
+ return this.acceptSpectatorsOrElse((x) -> x.reject(GameTexts.Join.spectatorsOnly()));
+ }
+
+ /**
+ * Returns an offer result that accepts this offer only if the intent is to spectate.
+ * Others will be resolved in the orElse method.
+ *
+ * This function does not do anything on its own, but its result must be returned within a
+ * {@link GamePlayerEvents#OFFER} listener.
+ *
+ * @return an "accept" offer result for spectators and "reject" offer result for participants
+ */
+ default JoinOfferResult acceptSpectatorsOrElse(Function function) {
+ return this.intent() == JoinIntent.SPECTATE ? this.accept() : function.apply(this);
+ }
+
+ /**
+ * Returns an offer result that accepts this offer only if the intent is to participate / play.
+ *
+ * This function does not do anything on its own, but its result must be returned within a
+ * {@link GamePlayerEvents#OFFER} listener.
+ *
+ * @return an "accept" offer result for participants and "reject" offer result for spectators
+ */
+ default JoinOfferResult acceptParticipants() {
+ return this.acceptParticipantsOrElse((x) -> x.reject(GameTexts.Join.participantsOnly()));
+ }
+
+ /**
+ * Returns an offer result that accepts this offer only if the intent is to participate / play.
+ * Others will be resolved in the orElse method.
+ *
+ * This function does not do anything on its own, but its result must be returned within a
+ * {@link GamePlayerEvents#OFFER} listener.
+ *
+ * @return an "accept" offer result for spectators and "reject" offer result for participants
+ */
+ default JoinOfferResult acceptParticipantsOrElse(Function function) {
+ return this.intent() == JoinIntent.PLAY ? this.accept() : function.apply(this);
+ }
}
diff --git a/src/main/java/xyz/nucleoid/plasmid/api/game/player/MutablePlayerSet.java b/src/main/java/xyz/nucleoid/plasmid/api/game/player/MutablePlayerSet.java
index a0508d64..da6c5f28 100644
--- a/src/main/java/xyz/nucleoid/plasmid/api/game/player/MutablePlayerSet.java
+++ b/src/main/java/xyz/nucleoid/plasmid/api/game/player/MutablePlayerSet.java
@@ -4,20 +4,32 @@
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.server.world.ServerWorld;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import xyz.nucleoid.plasmid.api.game.GameSpace;
import xyz.nucleoid.plasmid.api.util.PlayerRef;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;
+import java.util.function.Function;
public final class MutablePlayerSet implements PlayerSet {
- private final MinecraftServer server;
+ private final Function playerGetter;
private final Set players = new ObjectOpenHashSet<>();
public MutablePlayerSet(MinecraftServer server) {
- this.server = server;
+ this.playerGetter = server.getPlayerManager()::getPlayer;
+ }
+
+ public MutablePlayerSet(ServerWorld world) {
+ this.playerGetter = (uuid) -> world.getPlayerByUuid(uuid) instanceof ServerPlayerEntity player ? player : null;
+ }
+
+ public MutablePlayerSet(GameSpace gameSpace) {
+ this.playerGetter = gameSpace.getPlayers()::getEntity;
}
public void clear() {
@@ -47,7 +59,7 @@ public boolean remove(UUID id) {
@Nullable
@Override
public ServerPlayerEntity getEntity(UUID id) {
- return this.players.contains(id) ? this.server.getPlayerManager().getPlayer(id) : null;
+ return this.players.contains(id) ? this.playerGetter.apply(id) : null;
}
@Override
@@ -56,8 +68,8 @@ public boolean contains(UUID id) {
}
@Override
- public Iterator iterator() {
- var playerManager = this.server.getPlayerManager();
+ public @NotNull Iterator iterator() {
+ var playerGetter = this.playerGetter;
var ids = this.players.iterator();
return new AbstractIterator<>() {
@@ -65,7 +77,7 @@ public Iterator iterator() {
protected ServerPlayerEntity computeNext() {
while (ids.hasNext()) {
var id = ids.next();
- var player = playerManager.getPlayer(id);
+ var player = playerGetter.apply(id);
if (player != null) {
return player;
}
diff --git a/src/main/java/xyz/nucleoid/plasmid/api/game/player/PlayerSet.java b/src/main/java/xyz/nucleoid/plasmid/api/game/player/PlayerSet.java
index 0d8a3103..c257dc43 100644
--- a/src/main/java/xyz/nucleoid/plasmid/api/game/player/PlayerSet.java
+++ b/src/main/java/xyz/nucleoid/plasmid/api/game/player/PlayerSet.java
@@ -3,6 +3,7 @@
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.plasmid.api.util.PlayerRef;
import xyz.nucleoid.plasmid.impl.player.EmptyPlayerSet;
@@ -104,5 +105,6 @@ default MutablePlayerSet copy(MinecraftServer server) {
* @return an iterator over the online {@link ServerPlayerEntity} within this {@link PlayerSet}
*/
@Override
+ @NotNull
Iterator iterator();
}
diff --git a/src/main/java/xyz/nucleoid/plasmid/impl/game/manager/ManagedGameSpacePlayers.java b/src/main/java/xyz/nucleoid/plasmid/impl/game/manager/ManagedGameSpacePlayers.java
index ce981a10..23ff114f 100644
--- a/src/main/java/xyz/nucleoid/plasmid/impl/game/manager/ManagedGameSpacePlayers.java
+++ b/src/main/java/xyz/nucleoid/plasmid/impl/game/manager/ManagedGameSpacePlayers.java
@@ -1,6 +1,7 @@
package xyz.nucleoid.plasmid.impl.game.manager;
import net.minecraft.server.network.ServerPlayerEntity;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.plasmid.api.game.player.*;
import xyz.nucleoid.plasmid.impl.player.LocalJoinAcceptor;
@@ -174,7 +175,7 @@ public int size() {
}
@Override
- public Iterator iterator() {
+ public @NotNull Iterator iterator() {
return this.set.iterator();
}
diff --git a/src/main/java/xyz/nucleoid/plasmid/impl/player/ServerPlayerSet.java b/src/main/java/xyz/nucleoid/plasmid/impl/player/ServerPlayerSet.java
index aebe9d00..c54b8e2f 100644
--- a/src/main/java/xyz/nucleoid/plasmid/impl/player/ServerPlayerSet.java
+++ b/src/main/java/xyz/nucleoid/plasmid/impl/player/ServerPlayerSet.java
@@ -2,6 +2,7 @@
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.plasmid.api.game.player.PlayerSet;
@@ -26,7 +27,7 @@ public int size() {
}
@Override
- public Iterator iterator() {
+ public @NotNull Iterator iterator() {
return this.players.getPlayerList().iterator();
}
}
diff --git a/src/main/java/xyz/nucleoid/plasmid/impl/player/ServerWorldPlayerSet.java b/src/main/java/xyz/nucleoid/plasmid/impl/player/ServerWorldPlayerSet.java
index b87ea5d7..afe61428 100644
--- a/src/main/java/xyz/nucleoid/plasmid/impl/player/ServerWorldPlayerSet.java
+++ b/src/main/java/xyz/nucleoid/plasmid/impl/player/ServerWorldPlayerSet.java
@@ -2,6 +2,7 @@
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
+import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.nucleoid.plasmid.api.game.player.PlayerSet;
@@ -26,7 +27,7 @@ public int size() {
}
@Override
- public Iterator iterator() {
+ public @NotNull Iterator iterator() {
return this.world.getPlayers().iterator();
}
}
diff --git a/src/main/resources/data/plasmid/lang/en_us.json b/src/main/resources/data/plasmid/lang/en_us.json
index d0245fb9..f4294ab5 100644
--- a/src/main/resources/data/plasmid/lang/en_us.json
+++ b/src/main/resources/data/plasmid/lang/en_us.json
@@ -68,6 +68,8 @@
"text.plasmid.join_result.in_other_game": "You must %s before joining another game!",
"text.plasmid.join_result.in_other_game.leave_this_game": "leave this game",
"text.plasmid.join_result.not_allowed": "You are not allowed to join this game!",
+ "text.plasmid.join_result.spectators_only": "You are not allowed to participate in this game!",
+ "text.plasmid.join_result.participants_only": "You are not allowed to spectate this game!",
"text.plasmid.map_workspace.workspace_not_found": "Map with id '%s' was not found!",
"text.plasmid.map.bounds.get": "The bounds for the workspace are %s to %s",
"text.plasmid.map.bounds.set": "Updated bounds for workspace",