hollow-cube/add-gamemodechangeevent-and-fix-game-profile (#43)

* Resolve some todos and add connection.setProfile to EncryptionResponsePacket

* Add PlayerGameModeChangeEvent

(cherry picked from commit 1514d8ac1f)

* Add unit tests

(cherry picked from commit 9685e74f3b)

---------

Co-authored-by: GreatWyrm <alecmusante@gmail.com>
Co-authored-by: NxDs <7994264+NxDs@users.noreply.github.com>
This commit is contained in:
Matt Worzala 2023-08-05 12:53:57 -04:00 committed by GitHub
parent 28983ef364
commit 2c567696ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 127 additions and 15 deletions

View File

@ -1372,8 +1372,18 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
* Changes the player {@link GameMode}
*
* @param gameMode the new player GameMode
* @return true if the gamemode was changed successfully, false otherwise (cancelled by event)
*/
public void setGameMode(@NotNull GameMode gameMode) {
public boolean setGameMode(@NotNull GameMode gameMode) {
PlayerGameModeChangeEvent playerGameModeChangeEvent = new PlayerGameModeChangeEvent(this, gameMode);
EventDispatcher.call(playerGameModeChangeEvent);
if (playerGameModeChangeEvent.isCancelled()) {
// Abort
return false;
}
gameMode = playerGameModeChangeEvent.getNewGameMode();
this.gameMode = gameMode;
// Condition to prevent sending the packets before spawning the player
if (isActive()) {
@ -1405,6 +1415,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
if (isActive()) {
refreshAbilities();
}
return true;
}
/**

View File

@ -0,0 +1,56 @@
package net.minestom.server.event.player;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.event.trait.CancellableEvent;
import net.minestom.server.event.trait.PlayerInstanceEvent;
import org.jetbrains.annotations.NotNull;
/**
* Called when the gamemode of a player is being modified.
*/
public class PlayerGameModeChangeEvent implements PlayerInstanceEvent, CancellableEvent {
private final Player player;
private GameMode newGameMode;
private boolean cancelled;
public PlayerGameModeChangeEvent(@NotNull Player player, @NotNull GameMode newGameMode) {
this.player = player;
this.newGameMode = newGameMode;
}
/**
* Gets the target gamemode.
*
* @return the target gamemode
*/
public @NotNull GameMode getNewGameMode() {
return newGameMode;
}
/**
* Changes the target gamemode.
*
* @param newGameMode the new target gamemode
*/
public void setNewGameMode(@NotNull GameMode newGameMode) {
this.newGameMode = newGameMode;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@Override
public @NotNull Player getPlayer() {
return player;
}
}

View File

@ -1,12 +1,15 @@
package net.minestom.server.network.packet.client.login;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
import net.minestom.server.extras.MojangAuth;
import net.minestom.server.extras.mojangAuth.MojangCrypt;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.client.ClientPreplayPacket;
import net.minestom.server.network.player.GameProfile;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.network.player.PlayerSocketConnection;
import net.minestom.server.utils.async.AsyncUtils;
@ -20,7 +23,9 @@ import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import static net.minestom.server.network.NetworkBuffer.BYTE_ARRAY;
@ -72,14 +77,22 @@ public record EncryptionResponsePacket(byte[] sharedSecret,
client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).whenComplete((response, throwable) -> {
if (throwable != null) {
MinecraftServer.getExceptionManager().handleException(throwable);
//todo disconnect with reason
if (socketConnection.getPlayer() != null) {
socketConnection.getPlayer().kick(Component.text("Failed to contact Mojang's Session Servers (Are they down?)"));
} else {
socketConnection.disconnect();
}
return;
}
try {
final JsonObject gameProfile = GSON.fromJson(response.body(), JsonObject.class);
if (gameProfile == null) {
// Invalid response
//todo disconnect with reason
if (socketConnection.getPlayer() != null) {
socketConnection.getPlayer().kick(Component.text("Failed to get data from Mojang's Session Servers (Are they down?)"));
} else {
socketConnection.disconnect();
}
return;
}
socketConnection.setEncryptionKey(getSecretKey());
@ -89,6 +102,12 @@ public record EncryptionResponsePacket(byte[] sharedSecret,
MinecraftServer.LOGGER.info("UUID of player {} is {}", loginUsername, profileUUID);
CONNECTION_MANAGER.startPlayState(connection, profileUUID, profileName, true);
List<GameProfile.Property> propertyList = new ArrayList<>();
for (JsonElement element : gameProfile.get("properties").getAsJsonArray()) {
JsonObject object = element.getAsJsonObject();
propertyList.add(new GameProfile.Property(object.get("name").getAsString(), object.get("value").getAsString(), object.get("signature").getAsString()));
}
socketConnection.UNSAFE_setProfile(new GameProfile(profileUUID, profileName, propertyList));
} catch (Exception e) {
MinecraftServer.getExceptionManager().handleException(e);
}

View File

@ -1,9 +1,11 @@
package net.minestom.server.entity.player;
import net.minestom.server.event.player.PlayerGameModeChangeEvent;
import net.kyori.adventure.text.Component;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.message.ChatMessageType;
import net.minestom.server.network.packet.client.play.ClientSettingsPacket;
import net.minestom.server.event.player.PlayerGameModeChangeEvent;
import net.minestom.testing.Collector;
import net.minestom.testing.Env;
import net.minestom.testing.EnvTest;
@ -27,7 +29,8 @@ import static org.junit.jupiter.api.Assertions.*;
public class PlayerIntegrationTest {
/**
* Test to see whether player abilities are updated correctly when changing gamemodes
* Test to see whether player abilities are updated correctly and events
* are handled properly when changing gamemode.
*/
@Test
public void gamemodeTest(Env env) {
@ -36,16 +39,38 @@ public class PlayerIntegrationTest {
var player = connection.connect(instance, new Pos(0, 42, 0)).join();
assertEquals(instance, player.getInstance());
player.setGameMode(GameMode.CREATIVE);
assertAbilities(player, true, false, true, true);
player.setGameMode(GameMode.SPECTATOR);
assertAbilities(player, true, true, true, false);
player.setGameMode(GameMode.CREATIVE);
assertAbilities(player, true, true, true, true);
player.setGameMode(GameMode.ADVENTURE);
assertAbilities(player, false, false, false, false);
player.setGameMode(GameMode.SURVIVAL);
assertAbilities(player, false, false, false, false);
// Abilities
{
player.setGameMode(GameMode.CREATIVE);
assertAbilities(player, true, false, true, true);
player.setGameMode(GameMode.SPECTATOR);
assertAbilities(player, true, true, true, false);
player.setGameMode(GameMode.CREATIVE);
assertAbilities(player, true, true, true, true);
player.setGameMode(GameMode.ADVENTURE);
assertAbilities(player, false, false, false, false);
player.setGameMode(GameMode.SURVIVAL);
assertAbilities(player, false, false, false, false);
}
var listener = env.listen(PlayerGameModeChangeEvent.class);
// Normal change
{
listener.followup();
assertTrue(player.setGameMode(GameMode.ADVENTURE));
}
// Change target gamemode event
{
listener.followup(event -> event.setNewGameMode(GameMode.SPECTATOR));
assertTrue(player.setGameMode(GameMode.CREATIVE));
assertEquals(GameMode.SPECTATOR, player.getGameMode());
}
// Cancel event
{
listener.followup(event -> event.setCancelled(true));
assertFalse(player.setGameMode(GameMode.CREATIVE));
assertEquals(GameMode.SPECTATOR, player.getGameMode());
}
}
@Test
@ -165,7 +190,7 @@ public class PlayerIntegrationTest {
assertNull(player.getDeathLocation());
player.damage(DamageType.VOID, 30);
assertNotNull(player.getDeathLocation());
assertEquals(dimensionNamespace, player.getDeathLocation().dimension());
assertEquals(5, player.getDeathLocation().position().x());