Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Player join offer related changes #299

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,10 @@ However! Before we give functionality to our brilliant example game, we need to
An example offer listener may look like:
```java
activity.listen(GamePlayerEvents.OFFER, offer -> {
ServerPlayerEntity player = offer.player();
return offer.accept(world, new Vec3d(0.0, 64.0, 0.0))
.and(() -> {
player.changeGameMode(GameMode.ADVENTURE);
});
return offer.accept(world, new Vec3d(0.0, 65.0, 0.0))
.thenRunForEach(player -> {
player.changeGameMode(GameMode.ADVENTURE);
});
});
```

Expand Down Expand Up @@ -271,9 +270,8 @@ public final class ExampleGame {
}

private PlayerOfferResult onPlayerOffer(PlayerOffer offer) {
ServerPlayerEntity player = offer.player();
return offer.accept(this.world, new Vec3d(0.0, 64.0, 0.0))
.and(() -> {
return offer.accept(this.world, new Vec3d(0.0, 65.0, 0.0))
.thenRunForEach(player -> {
player.changeGameMode(GameMode.ADVENTURE);
});
}
Expand Down
16 changes: 7 additions & 9 deletions src/main/java/xyz/nucleoid/plasmid/command/GameCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -287,19 +287,17 @@ private static void joinAllPlayersToGame(ServerCommandSource source, GameSpace g
.collect(Collectors.toList());

var intent = JoinIntent.ANY;
var screen = gameSpace.getPlayers().screenJoins(players, intent);
if (screen.isOk()) {
for (var player : players) {
gameSpace.getPlayers().offer(player, intent);
}
} else {
source.sendError(screen.errorCopy().formatted(Formatting.RED));
var result = gameSpace.getPlayers().offer(players, intent);
if (result.isError()) {
source.sendError(result.errorCopy().formatted(Formatting.RED));
}
}

private static void tryJoinGame(ServerPlayerEntity player, GameSpace gameSpace) {
var results = GamePlayerJoiner.tryJoin(player, gameSpace, JoinIntent.ANY);
results.sendErrorsTo(player);
var result = GamePlayerJoiner.tryJoin(player, gameSpace, JoinIntent.ANY);
if (result.isError()) {
player.sendMessage(result.errorCopy().formatted(Formatting.RED));
}
}

private static GameSpace getJoinableGameSpace() throws CommandSyntaxException {
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/xyz/nucleoid/plasmid/command/ui/GameJoinUi.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ public GameJoinUi(ServerPlayerEntity player) {

private static void tryJoinGame(ServerPlayerEntity player, GameSpace gameSpace) {
player.server.execute(() -> {
var results = GamePlayerJoiner.tryJoin(player, gameSpace, JoinIntent.ANY);
results.sendErrorsTo(player);
var result = GamePlayerJoiner.tryJoin(player, gameSpace, JoinIntent.ANY);
if (result.isError()) {
player.sendMessage(result.errorCopy().formatted(Formatting.RED));
}
});
}

Expand Down
19 changes: 9 additions & 10 deletions src/main/java/xyz/nucleoid/plasmid/game/GameSpacePlayers.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,32 +17,31 @@
*/
public interface GameSpacePlayers extends PlayerSet {
/**
* Screens a group of players and returns whether the collective group should be allowed into the game.
* Simulates offer to join a player or group of players and returns whether they should be allowed into the game.
* <p>
* This logic is controlled through the active {@link GameActivity} through {@link GamePlayerEvents#SCREEN_JOINS}.
* This logic is controlled through the active {@link GameActivity} through {@link GamePlayerEvents#OFFER}.
*
* @param players the group of players trying to join
* @param intent the intent of the players trying to join, such as whether they want to participate or spectate
* @return a {@link GameResult} describing whether this group can join this game, or an error if not
* @see GamePlayerEvents#SCREEN_JOINS
* @see GameSpacePlayers#offer(ServerPlayerEntity, JoinIntent)
* @return a {@link GameResult} describing whether these players can join this game, or an error if not
* @see GameSpacePlayers#offer(Collection, JoinIntent)
* @see xyz.nucleoid.plasmid.game.player.GamePlayerJoiner
*/
GameResult screenJoins(Collection<ServerPlayerEntity> players, JoinIntent intent);
GameResult simulateOffer(Collection<ServerPlayerEntity> players, JoinIntent intent);

/**
* Offers an individual player to join this game. If accepted, they will be teleported into the game, and if not
* Offers a player or group of players to join this game. If accepted, they will be teleported into the game, and if not
* an error {@link GameResult} will be returned.
* <p>
* This logic is controlled through the active {@link GameActivity} through {@link GamePlayerEvents#OFFER}.
*
* @param player the player trying to join
* @param players the players trying to join
* @param intent the intent of the players trying to join, such as whether they want to participate or spectate
* @return a {@link GameResult} describing whether this player joined the game, or an error if not
* @return a {@link GameResult} describing whether these players joined the game, or an error if not
* @see GamePlayerEvents#OFFER
* @see xyz.nucleoid.plasmid.game.player.GamePlayerJoiner
*/
GameResult offer(ServerPlayerEntity player, JoinIntent intent);
GameResult offer(Collection<ServerPlayerEntity> players, JoinIntent intent);

/**
* Attempts to remove the given {@link ServerPlayerEntity} from this {@link GameSpace}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
import xyz.nucleoid.plasmid.game.event.GameActivityEvents;
import xyz.nucleoid.plasmid.game.event.GamePlayerEvents;
import xyz.nucleoid.plasmid.game.manager.GameSpaceManager;
import xyz.nucleoid.plasmid.game.player.PlayerOffer;
import xyz.nucleoid.plasmid.game.player.PlayerOfferResult;
import xyz.nucleoid.plasmid.game.player.JoinOffer;
import xyz.nucleoid.plasmid.game.player.JoinOfferResult;
import xyz.nucleoid.plasmid.game.rule.GameRuleType;
import xyz.nucleoid.plasmid.util.compatibility.AfkDisplayCompatibility;

Expand Down Expand Up @@ -89,7 +89,6 @@ public static GameWaitingLobby addTo(GameActivity activity, PlayerConfig playerC

activity.listen(GameActivityEvents.TICK, lobby::onTick);
activity.listen(GameActivityEvents.REQUEST_START, lobby::requestStart);
activity.listen(GamePlayerEvents.SCREEN_JOINS, (players, intent) -> lobby.screenJoins(players));
activity.listen(GamePlayerEvents.OFFER, lobby::offerPlayer);
activity.listen(GamePlayerEvents.REMOVE, lobby::onRemovePlayer);

Expand Down Expand Up @@ -170,17 +169,9 @@ private GameResult requestStart() {
}
}

private GameResult screenJoins(Collection<GameProfile> players) {
int newPlayerCount = this.gameSpace.getPlayers().size() + players.size();
private JoinOfferResult offerPlayer(JoinOffer offer) {
int newPlayerCount = this.gameSpace.getPlayers().size() + offer.players().size();
if (newPlayerCount > this.playerConfig.maxPlayers()) {
return GameResult.error(GameTexts.Join.gameFull());
}

return GameResult.ok();
}

private PlayerOfferResult offerPlayer(PlayerOffer offer) {
if (this.isFull()) {
return offer.reject(GameTexts.Join.gameFull());
}

Expand Down
62 changes: 28 additions & 34 deletions src/main/java/xyz/nucleoid/plasmid/game/event/GamePlayerEvents.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
package xyz.nucleoid.plasmid.game.event;

import com.mojang.authlib.GameProfile;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.math.Vec3d;
import xyz.nucleoid.plasmid.game.GameActivity;
import xyz.nucleoid.plasmid.game.GameResult;
import xyz.nucleoid.plasmid.game.GameSpace;
import xyz.nucleoid.plasmid.game.GameTexts;
import xyz.nucleoid.plasmid.game.player.JoinIntent;
import xyz.nucleoid.plasmid.game.player.PlayerOffer;
import xyz.nucleoid.plasmid.game.player.PlayerOfferResult;
import xyz.nucleoid.plasmid.game.player.JoinAcceptor;
import xyz.nucleoid.plasmid.game.player.JoinAcceptorResult;
import xyz.nucleoid.plasmid.game.player.JoinOffer;
import xyz.nucleoid.plasmid.game.player.JoinOfferResult;
import xyz.nucleoid.stimuli.event.StimulusEvent;

import java.util.Collection;

/**
* Events relating to players being added and removed from a {@link GameSpace} or {@link GameActivity}.
*/
Expand Down Expand Up @@ -102,53 +97,52 @@ public final class GamePlayerEvents {
});

/**
* Called when a group of players try to join this game. This should be used to reject multiple players as a group,
* such as when a party tries to join but has too many players to fit into the game.
* Called when a group of {@link ServerPlayerEntity} tries to join this game.
* <p>
* This is called before {@link GamePlayerEvents#OFFER} which handles specifically bringing a player into the game.
* Games must respond to this event in order for players to be able to join by returning either
* {@link JoinOffer#accept()} or {@link JoinOffer#reject(Text)}.
*
* @see GamePlayerEvents#OFFER
* @see JoinOffer
* @see JoinOfferResult
* @see GamePlayerEvents#ACCEPT
*/
public static final StimulusEvent<ScreenJoins> SCREEN_JOINS = StimulusEvent.create(ScreenJoins.class, ctx -> (players, intent) -> {
public static final StimulusEvent<Offer> OFFER = StimulusEvent.create(Offer.class, ctx -> offer -> {
try {
for (var listener : ctx.getListeners()) {
var result = listener.screenJoins(players, intent);
if (result.isError()) {
var result = listener.onOfferPlayers(offer);
if (!(result instanceof JoinOfferResult.Pass)) {
return result;
}
}
return GameResult.ok();
return offer.pass();
} catch (Throwable throwable) {
ctx.handleException(throwable);
return GameResult.error(GameTexts.Join.unexpectedError());
return offer.reject(GameTexts.Join.unexpectedError());
}
});

/**
* Called when a single {@link ServerPlayerEntity} tries to join this game. This event is responsible for bringing
* the player into the {@link GameSpace} world in the correct location.
* Called when a group of {@link ServerPlayerEntity} is accepted to join this game. This event is responsible for bringing
* the players into the {@link GameSpace} world in the correct location.
* <p>
* Games must respond to this event in order for a player to be able to join by returning either
* {@link PlayerOffer#accept(ServerWorld, Vec3d)} or {@link PlayerOffer#reject(Text)}.
* Games must respond to this event in order for players to be able to join.
*
* @see PlayerOffer
* @see PlayerOfferResult
* @see GamePlayerEvents#SCREEN_JOINS
* @see JoinAcceptor
* @see JoinAcceptorResult
* @see GamePlayerEvents#JOIN
*/
public static final StimulusEvent<Offer> OFFER = StimulusEvent.create(Offer.class, ctx -> offer -> {
public static final StimulusEvent<Accept> ACCEPT = StimulusEvent.create(Accept.class, ctx -> accept -> {
try {
for (var listener : ctx.getListeners()) {
var result = listener.onOfferPlayer(offer);
if (!(result instanceof PlayerOfferResult.Pass)) {
var result = listener.onAcceptPlayers(accept);
if (!(result instanceof JoinAcceptorResult.Pass)) {
return result;
}
}
return offer.pass();
} catch (Throwable throwable) {
ctx.handleException(throwable);
return offer.reject(GameTexts.Join.unexpectedError());
}
return accept.pass();
});

/**
Expand All @@ -175,12 +169,12 @@ public interface Remove {
void onRemovePlayer(ServerPlayerEntity player);
}

public interface ScreenJoins {
GameResult screenJoins(Collection<GameProfile> players, JoinIntent intent);
public interface Offer {
JoinOfferResult onOfferPlayers(JoinOffer offer);
}

public interface Offer {
PlayerOfferResult onOfferPlayer(PlayerOffer offer);
public interface Accept {
JoinAcceptorResult onAcceptPlayers(JoinAcceptor acceptor);
}

public interface Name {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package xyz.nucleoid.plasmid.game.manager;

import com.google.common.collect.Lists;
import com.mojang.authlib.GameProfile;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.registry.RegistryKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
Expand All @@ -19,11 +17,11 @@
import xyz.nucleoid.plasmid.game.config.GameConfig;
import xyz.nucleoid.plasmid.game.event.GameActivityEvents;
import xyz.nucleoid.plasmid.game.event.GamePlayerEvents;
import xyz.nucleoid.plasmid.game.player.JoinIntent;
import xyz.nucleoid.plasmid.game.player.LocalPlayerOffer;
import xyz.nucleoid.plasmid.game.player.PlayerOfferResult;
import xyz.nucleoid.plasmid.game.player.JoinAcceptorResult;
import xyz.nucleoid.plasmid.game.player.LocalJoinAcceptor;
import xyz.nucleoid.plasmid.game.player.LocalJoinOffer;
import xyz.nucleoid.plasmid.game.player.JoinOfferResult;

import java.util.Collection;
import java.util.Map;
import java.util.function.Consumer;

Expand Down Expand Up @@ -196,36 +194,23 @@ public GameBehavior getBehavior() {
return this.state;
}

GameResult screenJoins(Collection<ServerPlayerEntity> players, JoinIntent intent) {
var result = this.attemptScreenJoins(players.stream().map(PlayerEntity::getGameProfile).toList(), intent);

if (result.isError()) {
this.players.attemptGarbageCollection();
}

return result;
}

private GameResult attemptScreenJoins(Collection<GameProfile> players, JoinIntent intent) {
if (this.closed) {
return GameResult.error(GameTexts.Join.gameClosed());
}

return this.state.invoker(GamePlayerEvents.SCREEN_JOINS).screenJoins(players, intent);
}

PlayerOfferResult offerPlayer(LocalPlayerOffer offer) {
JoinOfferResult offerPlayers(LocalJoinOffer offer) {
if (this.closed) {
return offer.reject(GameTexts.Join.gameClosed());
} else if (this.manager.inGame(offer.player())) {
} else if (offer.serverPlayers().stream().anyMatch(this.manager::inGame)) {
return offer.reject(GameTexts.Join.inOtherGame());
} else if (!Permissions.check(offer.player(), "plasmid.join_game", true)) {
} else if (offer.serverPlayers().stream().anyMatch(p -> !Permissions.check(p, "plasmid.join_game", true))) {
return offer.reject(GameTexts.Join.notAllowed());
}

return this.state.invoker(GamePlayerEvents.OFFER).onOfferPlayer(offer);
return this.state.invoker(GamePlayerEvents.OFFER).onOfferPlayers(offer);
}

JoinAcceptorResult acceptPlayers(LocalJoinAcceptor acceptor) {
return this.state.invoker(GamePlayerEvents.ACCEPT).onAcceptPlayers(acceptor);
}


void onAddPlayer(ServerPlayerEntity player) {
this.state.propagatingInvoker(GamePlayerEvents.JOIN).onAddPlayer(player);
this.state.propagatingInvoker(GamePlayerEvents.ADD).onAddPlayer(player);
Expand Down
Loading
Loading