diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java
index 7871f00603c..0e7eab3513b 100644
--- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java
+++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java
@@ -25,17 +25,19 @@
package org.geysermc.platform.spigot.world;
+import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import lombok.AllArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.geysermc.connector.network.session.GeyserSession;
-import org.geysermc.connector.network.translators.world.WorldManager;
+import org.geysermc.connector.network.translators.world.GeyserWorldManager;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
+import org.geysermc.connector.utils.GameRule;
import us.myles.ViaVersion.protocols.protocol1_13_1to1_13.Protocol1_13_1To1_13;
import us.myles.ViaVersion.protocols.protocol1_16to1_15_2.data.MappingData;
@AllArgsConstructor
-public class GeyserSpigotWorldManager extends WorldManager {
+public class GeyserSpigotWorldManager extends GeyserWorldManager {
private final boolean isLegacy;
// You need ViaVersion to connect to an older server with Geyser.
@@ -69,4 +71,19 @@ public static int getLegacyBlock(GeyserSession session, int x, int y, int z, boo
return BlockTranslator.AIR;
}
}
+
+ @Override
+ public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
+ return Boolean.parseBoolean(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID()));
+ }
+
+ @Override
+ public int getGameRuleInt(GeyserSession session, GameRule gameRule) {
+ return Integer.parseInt(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID()));
+ }
+
+ @Override
+ public boolean hasPermission(GeyserSession session, String permission) {
+ return Bukkit.getPlayer(session.getPlayerEntity().getUsername()).hasPermission(permission);
+ }
}
diff --git a/connector/pom.xml b/connector/pom.xml
index 17c9711d9ae..ad8b82211af 100644
--- a/connector/pom.xml
+++ b/connector/pom.xml
@@ -96,6 +96,12 @@
8.3.1
compile
+
+ com.nukkitx.fastutil
+ fastutil-object-object-maps
+ 8.3.1
+ compile
+
com.google.guava
guava
diff --git a/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java b/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java
index eb8bf967e43..b6a766a3bec 100644
--- a/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java
+++ b/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java
@@ -30,14 +30,14 @@
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.GeyserLogger;
import org.geysermc.connector.command.CommandManager;
-import org.geysermc.connector.network.translators.world.CachedChunkManager;
+import org.geysermc.connector.network.translators.world.GeyserWorldManager;
import org.geysermc.connector.network.translators.world.WorldManager;
import java.nio.file.Path;
public interface GeyserBootstrap {
- CachedChunkManager DEFAULT_CHUNK_MANAGER = new CachedChunkManager();
+ GeyserWorldManager DEFAULT_CHUNK_MANAGER = new GeyserWorldManager();
/**
* Called when the GeyserBootstrap is enabled
diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
index 424f51870e8..52b2735137b 100644
--- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
+++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java
@@ -30,6 +30,7 @@
import com.github.steveice10.mc.protocol.data.message.TextMessage;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.math.vector.Vector3f;
+import com.nukkitx.protocol.bedrock.data.AdventureSetting;
import com.nukkitx.protocol.bedrock.data.AttributeData;
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
@@ -47,10 +48,7 @@
import org.geysermc.connector.utils.AttributeUtils;
import org.geysermc.connector.utils.MessageUtils;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
+import java.util.*;
import java.util.concurrent.TimeUnit;
@Getter @Setter
@@ -95,7 +93,7 @@ public void spawnEntity(GeyserSession session) {
addPlayerPacket.setMotion(motion);
addPlayerPacket.setHand(hand);
addPlayerPacket.getAdventureSettings().setCommandPermission(CommandPermission.NORMAL);
- addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.VISITOR);
+ addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.MEMBER);
addPlayerPacket.setDeviceId("");
addPlayerPacket.setPlatformChatId("");
addPlayerPacket.getMetadata().putAll(metadata);
@@ -212,7 +210,7 @@ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession s
if (entityMetadata.getId() == 2) {
// System.out.println(session.getScoreboardCache().getScoreboard().getObjectives().keySet());
- for (Team team : session.getScoreboardCache().getScoreboard().getTeams().values()) {
+ for (Team team : session.getWorldCache().getScoreboard().getTeams().values()) {
// session.getConnector().getLogger().info("team name " + team.getName());
// session.getConnector().getLogger().info("team entities " + team.getEntities());
}
@@ -221,7 +219,7 @@ public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession s
if (name != null) {
username = MessageUtils.getBedrockMessage(name);
}
- Team team = session.getScoreboardCache().getScoreboard().getTeamFor(username);
+ Team team = session.getWorldCache().getScoreboard().getTeamFor(username);
if (team != null) {
// session.getConnector().getLogger().info("team name es " + team.getName() + " with prefix " + team.getPrefix() + " and suffix " + team.getSuffix());
metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix());
diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java
index dd9f48d6a74..357e870f6a1 100644
--- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java
+++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java
@@ -34,6 +34,7 @@
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
import org.geysermc.connector.utils.LoginEncryptionUtils;
import org.geysermc.connector.utils.LanguageUtils;
+import org.geysermc.connector.utils.SettingsUtils;
public class UpstreamPacketHandler extends LoggingPacketHandler {
@@ -91,6 +92,10 @@ public boolean handle(ResourcePackClientResponsePacket packet) {
@Override
public boolean handle(ModalFormResponsePacket packet) {
+ if (packet.getFormId() == SettingsUtils.SETTINGS_FORM_ID) {
+ return SettingsUtils.handleSettingsForm(session, packet.getFormData());
+ }
+
return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormId(), packet.getFormData());
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
index 340292e254b..ac186a79905 100644
--- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
+++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java
@@ -46,6 +46,7 @@
import com.nukkitx.protocol.bedrock.BedrockPacket;
import com.nukkitx.protocol.bedrock.BedrockServerSession;
import com.nukkitx.protocol.bedrock.data.*;
+import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
import com.nukkitx.protocol.bedrock.packet.*;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
@@ -54,6 +55,7 @@
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import lombok.Getter;
import lombok.Setter;
+import org.geysermc.common.window.CustomFormWindow;
import org.geysermc.common.window.FormWindow;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.command.CommandSender;
@@ -79,9 +81,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
@Getter
@@ -102,7 +102,7 @@ public class GeyserSession implements CommandSender {
private ChunkCache chunkCache;
private EntityCache entityCache;
private InventoryCache inventoryCache;
- private ScoreboardCache scoreboardCache;
+ private WorldCache worldCache;
private WindowCache windowCache;
@Setter
private TeleportCache teleportCache;
@@ -191,6 +191,41 @@ public class GeyserSession implements CommandSender {
private MinecraftProtocol protocol;
+ private boolean reducedDebugInfo = false;
+
+ @Setter
+ private CustomFormWindow settingsForm;
+
+ /**
+ * The op permission level set by the server
+ */
+ @Setter
+ private int opPermissionLevel = 0;
+
+ /**
+ * If the current player can fly
+ */
+ @Setter
+ private boolean canFly = false;
+
+ /**
+ * If the current player is flying
+ */
+ @Setter
+ private boolean flying = false;
+
+ /**
+ * If the current player is in noclip
+ */
+ @Setter
+ private boolean noClip = false;
+
+ /**
+ * If the current player can not interact with the world
+ */
+ @Setter
+ private boolean worldImmutable = false;
+
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
this.connector = connector;
this.upstream = new UpstreamSession(bedrockServerSession);
@@ -198,7 +233,7 @@ public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServ
this.chunkCache = new ChunkCache(this);
this.entityCache = new EntityCache(this);
this.inventoryCache = new InventoryCache(this);
- this.scoreboardCache = new ScoreboardCache(this);
+ this.worldCache = new WorldCache(this);
this.windowCache = new WindowCache(this);
this.playerEntity = new PlayerEntity(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO);
@@ -440,7 +475,7 @@ public void disconnect(String reason) {
this.chunkCache = null;
this.entityCache = null;
- this.scoreboardCache = null;
+ this.worldCache = null;
this.inventoryCache = null;
this.windowCache = null;
@@ -605,4 +640,66 @@ public void sendDownstreamPacket(Packet packet) {
connector.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server");
}
}
+
+ /**
+ * Update the cached value for the reduced debug info gamerule.
+ * This also toggles the coordinates display
+ *
+ * @param value The new value for reducedDebugInfo
+ */
+ public void setReducedDebugInfo(boolean value) {
+ worldCache.setShowCoordinates(!value);
+ reducedDebugInfo = value;
+ }
+
+ /**
+ * Send a gamerule value to the client
+ *
+ * @param gameRule The gamerule to send
+ * @param value The value of the gamerule
+ */
+ public void sendGameRule(String gameRule, Object value) {
+ GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket();
+ gameRulesChangedPacket.getGameRules().add(new GameRuleData<>(gameRule, value));
+ upstream.sendPacket(gameRulesChangedPacket);
+ }
+
+ /**
+ * @see org.geysermc.connector.network.translators.world.WorldManager#hasPermission(GeyserSession, String)
+ */
+ public Boolean hasPermission(String permission) {
+ return connector.getWorldManager().hasPermission(this, permission);
+ }
+
+ /**
+ * Send an AdventureSettingsPacket to the client with the latest flags
+ */
+ public void sendAdventureSettings() {
+ AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket();
+ adventureSettingsPacket.setUniqueEntityId(playerEntity.getGeyserId());
+ adventureSettingsPacket.setCommandPermission(CommandPermission.NORMAL);
+ adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER);
+
+ Set flags = new HashSet<>();
+ if (canFly) {
+ flags.add(AdventureSetting.MAY_FLY);
+ }
+
+ if (flying) {
+ flags.add(AdventureSetting.FLYING);
+ }
+
+ if (worldImmutable) {
+ flags.add(AdventureSetting.WORLD_IMMUTABLE);
+ }
+
+ if (noClip) {
+ flags.add(AdventureSetting.NO_CLIP);
+ }
+
+ flags.add(AdventureSetting.AUTO_JUMP);
+
+ adventureSettingsPacket.getSettings().addAll(flags);
+ sendUpstreamPacket(adventureSettingsPacket);
+ }
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ScoreboardCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java
similarity index 78%
rename from connector/src/main/java/org/geysermc/connector/network/session/cache/ScoreboardCache.java
rename to connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java
index 9a6924075ef..310e5f9d7f7 100644
--- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ScoreboardCache.java
+++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java
@@ -25,7 +25,9 @@
package org.geysermc.connector.network.session.cache;
+import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
import lombok.Getter;
+import lombok.Setter;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.scoreboard.Objective;
import org.geysermc.connector.scoreboard.Scoreboard;
@@ -33,11 +35,18 @@
import java.util.Collection;
@Getter
-public class ScoreboardCache {
+public class WorldCache {
+
private GeyserSession session;
+
+ @Setter
+ private Difficulty difficulty = Difficulty.EASY;
+
+ private boolean showCoordinates = true;
+
private Scoreboard scoreboard;
- public ScoreboardCache(GeyserSession session) {
+ public WorldCache(GeyserSession session) {
this.session = session;
this.scoreboard = new Scoreboard(session);
}
@@ -52,4 +61,14 @@ public void removeScoreboard() {
}
}
}
+
+ /**
+ * Tell the client to hide or show the coordinates
+ *
+ * @param value True to show, false to hide
+ */
+ public void setShowCoordinates(boolean value) {
+ showCoordinates = value;
+ session.sendGameRule("showcoordinates", value);
+ }
}
\ No newline at end of file
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/CachedChunkManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java
similarity index 55%
rename from connector/src/main/java/org/geysermc/connector/network/translators/world/CachedChunkManager.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java
index 0580fcffdac..a8591cd7f90 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/CachedChunkManager.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java
@@ -23,15 +23,25 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.world;
+package org.geysermc.connector.network.translators.bedrock;
-import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.nukkitx.protocol.bedrock.packet.ServerSettingsRequestPacket;
+import com.nukkitx.protocol.bedrock.packet.ServerSettingsResponsePacket;
import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.network.translators.PacketTranslator;
+import org.geysermc.connector.network.translators.Translator;
+import org.geysermc.connector.utils.SettingsUtils;
-public class CachedChunkManager extends WorldManager {
+@Translator(packet = ServerSettingsRequestPacket.class)
+public class BedrockServerSettingsRequestTranslator extends PacketTranslator {
@Override
- public int getBlockAt(GeyserSession session, int x, int y, int z) {
- return session.getChunkCache().getBlockAt(new Position(x, y, z));
+ public void translate(ServerSettingsRequestPacket packet, GeyserSession session) {
+ SettingsUtils.buildForm(session);
+
+ ServerSettingsResponsePacket serverSettingsResponsePacket = new ServerSettingsResponsePacket();
+ serverSettingsResponsePacket.setFormData(session.getSettingsForm().getJSONData());
+ serverSettingsResponsePacket.setFormId(SettingsUtils.SETTINGS_FORM_ID);
+ session.sendUpstreamPacket(serverSettingsResponsePacket);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityEventTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java
similarity index 98%
rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityEventTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java
index 620e2b8a51e..18fd6614ee1 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityEventTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.bedrock;
+package org.geysermc.connector.network.translators.bedrock.entity;
import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade;
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java
similarity index 99%
rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java
index 2e1a122ec07..f4365f7963e 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.bedrock;
+package org.geysermc.connector.network.translators.bedrock.entity.player;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java
similarity index 96%
rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java
index f07016e701f..e76fece0b59 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.bedrock;
+package org.geysermc.connector.network.translators.bedrock.entity.player;
import com.nukkitx.protocol.bedrock.packet.EmotePacket;
import org.geysermc.connector.GeyserConnector;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java
similarity index 99%
rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java
index 856b01eecb9..c5d6f2dda7a 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.bedrock;
+package org.geysermc.connector.network.translators.bedrock.entity.player;
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java
similarity index 98%
rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java
index 0abf8150571..be918ba74c4 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.bedrock;
+package org.geysermc.connector.network.translators.bedrock.entity.player;
import com.nukkitx.math.vector.Vector3d;
import org.geysermc.connector.common.ChatColor;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLevelSoundEventTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/world/BedrockLevelSoundEventTranslator.java
similarity index 97%
rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLevelSoundEventTranslator.java
rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/world/BedrockLevelSoundEventTranslator.java
index 08ad10bfbc8..44553e82e28 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLevelSoundEventTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/world/BedrockLevelSoundEventTranslator.java
@@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
-package org.geysermc.connector.network.translators.bedrock;
+package org.geysermc.connector.network.translators.bedrock.world;
import com.nukkitx.protocol.bedrock.data.SoundEvent;
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java
index 7e4d9ca8622..601b0fc488c 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java
@@ -40,5 +40,7 @@ public void translate(ServerDifficultyPacket packet, GeyserSession session) {
SetDifficultyPacket setDifficultyPacket = new SetDifficultyPacket();
setDifficultyPacket.setDifficulty(packet.getDifficulty().ordinal());
session.sendUpstreamPacket(setDifficultyPacket);
+
+ session.getWorldCache().setDifficulty(packet.getDifficulty());
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java
index 94b5bed38d0..da107f48df5 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java
@@ -56,7 +56,7 @@ public void translate(ServerJoinGamePacket packet, GeyserSession session) {
DimensionUtils.switchDimension(session, fakeDim);
DimensionUtils.switchDimension(session, packet.getDimension());
- session.getScoreboardCache().removeScoreboard();
+ session.getWorldCache().removeScoreboard();
}
AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket();
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java
index 109e00e29dc..ac2a80f7a87 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java
@@ -31,6 +31,7 @@
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.entity.type.EntityType;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
@@ -51,6 +52,33 @@ public void translate(ServerEntityStatusPacket packet, GeyserSession session) {
EntityEventPacket entityEventPacket = new EntityEventPacket();
entityEventPacket.setRuntimeEntityId(entity.getGeyserId());
switch (packet.getStatus()) {
+ case PLAYER_ENABLE_REDUCED_DEBUG:
+ session.setReducedDebugInfo(true);
+ return;
+ case PLAYER_DISABLE_REDUCED_DEBUG:
+ session.setReducedDebugInfo(false);
+ return;
+ case PLAYER_OP_PERMISSION_LEVEL_0:
+ session.setOpPermissionLevel(0);
+ session.sendAdventureSettings();
+ return;
+ case PLAYER_OP_PERMISSION_LEVEL_1:
+ session.setOpPermissionLevel(1);
+ session.sendAdventureSettings();
+ return;
+ case PLAYER_OP_PERMISSION_LEVEL_2:
+ session.setOpPermissionLevel(2);
+ session.sendAdventureSettings();
+ return;
+ case PLAYER_OP_PERMISSION_LEVEL_3:
+ session.setOpPermissionLevel(3);
+ session.sendAdventureSettings();
+ return;
+ case PLAYER_OP_PERMISSION_LEVEL_4:
+ session.setOpPermissionLevel(4);
+ session.sendAdventureSettings();
+ return;
+
// EntityEventType.HURT sends extra data depending on the type of damage. However this appears to have no visual changes
case LIVING_BURN:
case LIVING_DROWN:
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerAbilitiesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerAbilitiesTranslator.java
index a569acff802..0f5a12e5add 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerAbilitiesTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerAbilitiesTranslator.java
@@ -32,6 +32,7 @@
import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import org.geysermc.connector.entity.Entity;
+import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
@@ -43,24 +44,12 @@ public class JavaPlayerAbilitiesTranslator extends PacketTranslator playerFlags = new ObjectOpenHashSet<>();
- playerFlags.add(AdventureSetting.AUTO_JUMP);
- if (packet.isCanFly())
- playerFlags.add(AdventureSetting.MAY_FLY);
-
- if (packet.isFlying())
- playerFlags.add(AdventureSetting.FLYING);
-
- AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket();
- adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER);
- // Required or the packet simply is not sent
- adventureSettingsPacket.setCommandPermission(CommandPermission.NORMAL);
- adventureSettingsPacket.setUniqueEntityId(entity.getGeyserId());
- adventureSettingsPacket.getSettings().addAll(playerFlags);
- session.sendUpstreamPacket(adventureSettingsPacket);
+ session.setCanFly(packet.isCanFly());
+ session.setFlying(packet.isFlying());
+ session.sendAdventureSettings();
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java
index 5a722953ac9..3ee174d7a8e 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java
@@ -36,7 +36,7 @@ public class JavaDisplayScoreboardTranslator extends PacketTranslator {
public void translate(ServerTeamPacket packet, GeyserSession session) {
GeyserConnector.getInstance().getLogger().debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers()));
- Scoreboard scoreboard = session.getScoreboardCache().getScoreboard();
+ Scoreboard scoreboard = session.getWorldCache().getScoreboard();
Team team = scoreboard.getTeam(packet.getTeamName());
switch (packet.getAction()) {
case CREATE:
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java
index 827e4c7f4b9..8d7d59a892c 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java
@@ -42,7 +42,7 @@ public class JavaUpdateScoreTranslator extends PacketTranslator playerFlags = new ObjectOpenHashSet<>();
GameMode gameMode = (GameMode) packet.getValue();
- if (gameMode == GameMode.ADVENTURE)
- playerFlags.add(AdventureSetting.WORLD_IMMUTABLE);
- if (gameMode == GameMode.CREATIVE)
- playerFlags.add(AdventureSetting.MAY_FLY);
-
- if (gameMode == GameMode.SPECTATOR) {
- playerFlags.add(AdventureSetting.MAY_FLY);
- playerFlags.add(AdventureSetting.NO_CLIP);
- playerFlags.add(AdventureSetting.FLYING);
- playerFlags.add(AdventureSetting.WORLD_IMMUTABLE);
- gameMode = GameMode.CREATIVE; // spectator doesnt exist on bedrock
- }
-
- playerFlags.add(AdventureSetting.AUTO_JUMP);
+ session.setNoClip(gameMode == GameMode.SPECTATOR);
+ session.setWorldImmutable(gameMode == GameMode.ADVENTURE || gameMode == GameMode.SPECTATOR);
+ session.sendAdventureSettings();
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
playerGameTypePacket.setGamemode(gameMode.ordinal());
session.sendUpstreamPacket(playerGameTypePacket);
session.setGameMode(gameMode);
- // We need to delay this because otherwise it's overridden by the adventure settings from the abilities packet
- session.getConnector().getGeneralThreadPool().schedule(() -> {
- AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket();
- adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER);
- adventureSettingsPacket.setCommandPermission(CommandPermission.NORMAL);
- adventureSettingsPacket.setUniqueEntityId(entity.getGeyserId());
- adventureSettingsPacket.getSettings().addAll(playerFlags);
- session.sendUpstreamPacket(adventureSettingsPacket);
- }, 50, TimeUnit.MILLISECONDS);
-
// Update the crafting grid to add/remove barriers for creative inventory
PlayerInventoryTranslator.updateCraftingGrid(session, session.getInventory());
break;
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java
index 188e960d6d5..8dc68918550 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java
@@ -67,9 +67,7 @@ public void translate(ServerUpdateTimePacket packet, GeyserSession session) {
}
private void setDoDaylightCycleGamerule(GeyserSession session, boolean doCycle) {
- GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket();
- gameRulesChangedPacket.getGameRules().add(new GameRuleData<>("dodaylightcycle", doCycle));
- session.sendUpstreamPacket(gameRulesChangedPacket);
+ session.sendGameRule("dodaylightcycle", doCycle);
}
}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java
new file mode 100644
index 00000000000..83f1a7783f8
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.network.translators.world;
+
+import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
+import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
+import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket;
+import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.utils.GameRule;
+
+public class GeyserWorldManager extends WorldManager {
+
+ private static final Object2ObjectMap gameruleCache = new Object2ObjectOpenHashMap<>();
+
+ @Override
+ public int getBlockAt(GeyserSession session, int x, int y, int z) {
+ return session.getChunkCache().getBlockAt(new Position(x, y, z));
+ }
+
+ @Override
+ public void setGameRule(GeyserSession session, String name, Object value) {
+ session.sendDownstreamPacket(new ClientChatPacket("/gamerule " + name + " " + value));
+ gameruleCache.put(name, String.valueOf(value));
+ }
+
+ @Override
+ public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) {
+ String value = gameruleCache.get(gameRule.getJavaID());
+ if (value != null) {
+ return Boolean.parseBoolean(value);
+ }
+
+ return gameRule.getDefaultValue() != null ? (Boolean) gameRule.getDefaultValue() : false;
+ }
+
+ @Override
+ public int getGameRuleInt(GeyserSession session, GameRule gameRule) {
+ String value = gameruleCache.get(gameRule.getJavaID());
+ if (value != null) {
+ return Integer.parseInt(value);
+ }
+
+ return gameRule.getDefaultValue() != null ? (int) gameRule.getDefaultValue() : 0;
+ }
+
+ @Override
+ public void setPlayerGameMode(GeyserSession session, GameMode gameMode) {
+ session.sendDownstreamPacket(new ClientChatPacket("/gamemode " + gameMode.name().toLowerCase()));
+ }
+
+ @Override
+ public void setDifficulty(GeyserSession session, Difficulty difficulty) {
+ session.sendDownstreamPacket(new ClientChatPacket("/difficulty " + difficulty.name().toLowerCase()));
+ }
+
+ @Override
+ public boolean hasPermission(GeyserSession session, String permission) {
+ return false;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java
index 325e686091c..3260122778b 100644
--- a/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java
+++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java
@@ -26,8 +26,11 @@
package org.geysermc.connector.network.translators.world;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
+import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
+import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
import com.nukkitx.math.vector.Vector3i;
import org.geysermc.connector.network.session.GeyserSession;
+import org.geysermc.connector.utils.GameRule;
/**
* Class that manages or retrieves various information
@@ -70,4 +73,56 @@ public int getBlockAt(GeyserSession session, Vector3i vector) {
* @return the block state at the specified location
*/
public abstract int getBlockAt(GeyserSession session, int x, int y, int z);
+
+ /**
+ * Updates a gamerule value on the Java server
+ *
+ * @param session The session of the user that requested the change
+ * @param name The gamerule to change
+ * @param value The new value for the gamerule
+ */
+ public abstract void setGameRule(GeyserSession session, String name, Object value);
+
+ /**
+ * Get a gamerule value as a boolean
+ *
+ * @param session The session of the user that requested the value
+ * @param gameRule The gamerule to fetch the value of
+ * @return The boolean representation of the value
+ */
+ public abstract Boolean getGameRuleBool(GeyserSession session, GameRule gameRule);
+
+ /**
+ * Get a gamerule value as an integer
+ *
+ * @param session The session of the user that requested the value
+ * @param gameRule The gamerule to fetch the value of
+ * @return The integer representation of the value
+ */
+ public abstract int getGameRuleInt(GeyserSession session, GameRule gameRule);
+
+ /**
+ * Change the game mode of the given session
+ *
+ * @param session The session of the player to change the game mode of
+ * @param gameMode The game mode to change the player to
+ */
+ public abstract void setPlayerGameMode(GeyserSession session, GameMode gameMode);
+
+ /**
+ * Change the difficulty of the Java server
+ *
+ * @param session The session of the user that requested the change
+ * @param difficulty The difficulty to change to
+ */
+ public abstract void setDifficulty(GeyserSession session, Difficulty difficulty);
+
+ /**
+ * Checks if the given session's player has a permission
+ *
+ * @param session The session of the player to check the permission of
+ * @param permission The permission node to check
+ * @return True if the player has the requested permission, false if not
+ */
+ public abstract boolean hasPermission(GeyserSession session, String permission);
}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/GameRule.java b/connector/src/main/java/org/geysermc/connector/utils/GameRule.java
new file mode 100644
index 00000000000..350337f3d7e
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/GameRule.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.utils;
+
+import lombok.Getter;
+
+/**
+ * This enum stores each gamerule along with the value type and the default.
+ * It is used to construct the list for the settings menu
+ */
+public enum GameRule {
+ ANNOUNCEADVANCEMENTS("announceAdvancements", Boolean.class, true), // JE only
+ COMMANDBLOCKOUTPUT("commandBlockOutput", Boolean.class, true),
+ DISABLEELYTRAMOVEMENTCHECK("disableElytraMovementCheck", Boolean.class, false), // JE only
+ DISABLERAIDS("disableRaids", Boolean.class, false), // JE only
+ DODAYLIGHTCYCLE("doDaylightCycle", Boolean.class, true),
+ DOENTITYDROPS("doEntityDrops", Boolean.class, true),
+ DOFIRETICK("doFireTick", Boolean.class, true),
+ DOIMMEDIATERESPAWN("doImmediateRespawn", Boolean.class, false),
+ DOINSOMNIA("doInsomnia", Boolean.class, true),
+ DOLIMITEDCRAFTING("doLimitedCrafting", Boolean.class, false), // JE only
+ DOMOBLOOT("doMobLoot", Boolean.class, true),
+ DOMOBSPAWNING("doMobSpawning", Boolean.class, true),
+ DOPATROLSPAWNING("doPatrolSpawning", Boolean.class, true), // JE only
+ DOTILEDROPS("doTileDrops", Boolean.class, true),
+ DOTRADERSPAWNING("doTraderSpawning", Boolean.class, true), // JE only
+ DOWEATHERCYCLE("doWeatherCycle", Boolean.class, true),
+ DROWNINGDAMAGE("drowningDamage", Boolean.class, true),
+ FALLDAMAGE("fallDamage", Boolean.class, true),
+ FIREDAMAGE("fireDamage", Boolean.class, true),
+ FORGIVEDEADPLAYERS("forgiveDeadPlayers", Boolean.class, true), // JE only
+ KEEPINVENTORY("keepInventory", Boolean.class, false),
+ LOGADMINCOMMANDS("logAdminCommands", Boolean.class, true), // JE only
+ MAXCOMMANDCHAINLENGTH("maxCommandChainLength", Integer.class, 65536),
+ MAXENTITYCRAMMING("maxEntityCramming", Integer.class, 24), // JE only
+ MOBGRIEFING("mobGriefing", Boolean.class, true),
+ NATURALREGENERATION("naturalRegeneration", Boolean.class, true),
+ RANDOMTICKSPEED("randomTickSpeed", Integer.class, 3),
+ REDUCEDDEBUGINFO("reducedDebugInfo", Boolean.class, false), // JE only
+ SENDCOMMANDFEEDBACK("sendCommandFeedback", Boolean.class, true),
+ SHOWDEATHMESSAGES("showDeathMessages", Boolean.class, true),
+ SPAWNRADIUS("spawnRadius", Integer.class, 10),
+ SPECTATORSGENERATECHUNKS("spectatorsGenerateChunks", Boolean.class, true), // JE only
+ UNIVERSALANGER("universalAnger", Boolean.class, false), // JE only
+
+ UNKNOWN("unknown", Object.class);
+
+ private static final GameRule[] VALUES = values();
+
+ @Getter
+ private String javaID;
+
+ @Getter
+ private Class> type;
+
+ @Getter
+ private Object defaultValue;
+
+ GameRule(String javaID, Class> type) {
+ this(javaID, type, null);
+ }
+
+ GameRule(String javaID, Class> type, Object defaultValue) {
+ this.javaID = javaID;
+ this.type = type;
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * Convert a string to an object of the correct type for the current gamerule
+ *
+ * @param value The string value to convert
+ * @return The converted and formatted value
+ */
+ public Object convertValue(String value) {
+ if (type.equals(Boolean.class)) {
+ return Boolean.parseBoolean(value);
+ } else if (type.equals(Integer.class)) {
+ return Integer.parseInt(value);
+ }
+
+ return null;
+ }
+
+ /**
+ * Fetch a game rule by the given Java ID
+ *
+ * @param id The ID of the gamerule
+ * @return A {@link GameRule} object representing the requested ID or {@link GameRule.UNKNOWN}
+ */
+ public static GameRule fromJavaID(String id) {
+ for (GameRule gamerule : VALUES) {
+ if (gamerule.javaID.equals(id)) {
+ return gamerule;
+ }
+ }
+
+ return UNKNOWN;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java
new file mode 100644
index 00000000000..89e9fe67b7f
--- /dev/null
+++ b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * @author GeyserMC
+ * @link https://github.com/GeyserMC/Geyser
+ */
+
+package org.geysermc.connector.utils;
+
+import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
+import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
+import org.geysermc.common.window.CustomFormBuilder;
+import org.geysermc.common.window.CustomFormWindow;
+import org.geysermc.common.window.button.FormImage;
+import org.geysermc.common.window.component.DropdownComponent;
+import org.geysermc.common.window.component.InputComponent;
+import org.geysermc.common.window.component.LabelComponent;
+import org.geysermc.common.window.component.ToggleComponent;
+import org.geysermc.common.window.response.CustomFormResponse;
+import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.network.session.GeyserSession;
+
+import java.util.ArrayList;
+
+public class SettingsUtils {
+
+ // Used in UpstreamPacketHandler.java
+ public static final int SETTINGS_FORM_ID = 1338;
+
+ /**
+ * Build a settings form for the given session and store it for later
+ *
+ * @param session The session to build the form for
+ */
+ public static void buildForm(GeyserSession session) {
+ // Cache the language for cleaner access
+ String language = session.getClientData().getLanguageCode();
+
+ CustomFormBuilder builder = new CustomFormBuilder(LanguageUtils.getPlayerLocaleString("geyser.settings.title.main", language));
+ builder.setIcon(new FormImage(FormImage.FormImageType.PATH, "textures/ui/settings_glyph_color_2x.png"));
+
+ builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.client", language)));
+ builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language, session.getWorldCache().isShowCoordinates())));
+
+
+ if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) {
+ builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.server", language)));
+
+ DropdownComponent gamemodeDropdown = new DropdownComponent();
+ gamemodeDropdown.setText("%createWorldScreen.gameMode.personal");
+ gamemodeDropdown.setOptions(new ArrayList<>());
+ for (GameMode gamemode : GameMode.values()) {
+ gamemodeDropdown.addOption(LocaleUtils.getLocaleString("selectWorld.gameMode." + gamemode.name().toLowerCase(), language), session.getGameMode() == gamemode);
+ }
+ builder.addComponent(gamemodeDropdown);
+
+ DropdownComponent difficultyDropdown = new DropdownComponent();
+ difficultyDropdown.setText("%options.difficulty");
+ difficultyDropdown.setOptions(new ArrayList<>());
+ for (Difficulty difficulty : Difficulty.values()) {
+ difficultyDropdown.addOption("%options.difficulty." + difficulty.name().toLowerCase(), session.getWorldCache().getDifficulty() == difficulty);
+ }
+ builder.addComponent(difficultyDropdown);
+ }
+
+ if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.gamerules")) {
+ builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.game_rules", language)));
+ for (GameRule gamerule : GameRule.values()) {
+ if (gamerule.equals(GameRule.UNKNOWN)) {
+ continue;
+ }
+
+ // Add the relevant form item based on the gamerule type
+ if (Boolean.class.equals(gamerule.getType())) {
+ builder.addComponent(new ToggleComponent(LocaleUtils.getLocaleString("gamerule." + gamerule.getJavaID(), language), GeyserConnector.getInstance().getWorldManager().getGameRuleBool(session, gamerule)));
+ } else if (Integer.class.equals(gamerule.getType())) {
+ builder.addComponent(new InputComponent(LocaleUtils.getLocaleString("gamerule." + gamerule.getJavaID(), language), "", String.valueOf(GeyserConnector.getInstance().getWorldManager().getGameRuleInt(session, gamerule))));
+ }
+ }
+ }
+
+ session.setSettingsForm(builder.build());
+ }
+
+ /**
+ * Handle the settings form response
+ *
+ * @param session The session that sent the response
+ * @param response The response string to parse
+ * @return True if the form was parsed correctly, false if not
+ */
+ public static boolean handleSettingsForm(GeyserSession session, String response) {
+ CustomFormWindow settingsForm = session.getSettingsForm();
+ settingsForm.setResponse(response);
+
+ CustomFormResponse settingsResponse = (CustomFormResponse) settingsForm.getResponse();
+ int offset = 0;
+
+ offset++; // Client settings title
+
+ session.getWorldCache().setShowCoordinates(settingsResponse.getToggleResponses().get(offset));
+ offset++;
+
+ if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) {
+ offset++; // Server settings title
+
+ GameMode gameMode = GameMode.values()[settingsResponse.getDropdownResponses().get(offset).getElementID()];
+ if (gameMode != null && gameMode != session.getGameMode()) {
+ session.getConnector().getWorldManager().setPlayerGameMode(session, gameMode);
+ }
+ offset++;
+
+ Difficulty difficulty = Difficulty.values()[settingsResponse.getDropdownResponses().get(offset).getElementID()];
+ if (difficulty != null && difficulty != session.getWorldCache().getDifficulty()) {
+ session.getConnector().getWorldManager().setDifficulty(session, difficulty);
+ }
+ offset++;
+ }
+
+ if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.gamerules")) {
+ offset++; // Game rule title
+
+ for (GameRule gamerule : GameRule.values()) {
+ if (gamerule.equals(GameRule.UNKNOWN)) {
+ continue;
+ }
+
+ if (Boolean.class.equals(gamerule.getType())) {
+ Boolean value = settingsResponse.getToggleResponses().get(offset).booleanValue();
+ if (value != session.getConnector().getWorldManager().getGameRuleBool(session, gamerule)) {
+ session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value);
+ }
+ } else if (Integer.class.equals(gamerule.getType())) {
+ int value = Integer.parseInt(settingsResponse.getInputResponses().get(offset));
+ if (value != session.getConnector().getWorldManager().getGameRuleInt(session, gamerule)) {
+ session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value);
+ }
+ }
+ offset++;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
index 77ab7f9393c..1d5e4073de0 100644
--- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
+++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java
@@ -35,6 +35,7 @@
import lombok.Getter;
import org.geysermc.connector.common.AuthType;
import org.geysermc.connector.GeyserConnector;
+import org.geysermc.connector.entity.Entity;
import org.geysermc.connector.entity.PlayerEntity;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.session.auth.BedrockClientData;
@@ -93,6 +94,15 @@ public static PlayerListPacket.Entry buildEntryManually(GeyserSession session, U
ImageData.of(capeData), geometryData, "", true, false, !capeId.equals(SkinProvider.EMPTY_CAPE.getCapeId()), capeId, skinId
);
+ // This attempts to find the xuid of the player so profile images show up for xbox accounts
+ String xuid = "";
+ for (GeyserSession player : GeyserConnector.getInstance().getPlayers()) {
+ if (player.getPlayerEntity().getUuid().equals(uuid)) {
+ xuid = player.getAuthData().getXboxUUID();
+ break;
+ }
+ }
+
PlayerListPacket.Entry entry;
// If we are building a PlayerListEntry for our own session we use our AuthData UUID instead of the Java UUID
@@ -102,11 +112,11 @@ public static PlayerListPacket.Entry buildEntryManually(GeyserSession session, U
} else {
entry = new PlayerListPacket.Entry(uuid);
}
-
+
entry.setName(username);
entry.setEntityId(geyserId);
entry.setSkin(serializedSkin);
- entry.setXuid("");
+ entry.setXuid(xuid);
entry.setPlatformChatId("");
entry.setTeacher(false);
entry.setTrustedSkin(true);