forked from GeyserMC/Geyser
Merge branch 'master' of https://github.com/GeyserMC/Geyser into feature/1.16.2
This commit is contained in:
commit
2dc71382e7
47 changed files with 890 additions and 237 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -241,3 +241,4 @@ config.yml
|
||||||
logs/
|
logs/
|
||||||
public-key.pem
|
public-key.pem
|
||||||
locales/
|
locales/
|
||||||
|
cache/
|
|
@ -39,6 +39,9 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set
|
||||||
- [ ] Beacon
|
- [ ] Beacon
|
||||||
- [ ] Cartography Table
|
- [ ] Cartography Table
|
||||||
- [ ] Stonecutter
|
- [ ] Stonecutter
|
||||||
|
- [ ] Command Block
|
||||||
|
- [ ] Structure Block
|
||||||
|
- [ ] Horse Inventory
|
||||||
- Some Entity Flags
|
- Some Entity Flags
|
||||||
|
|
||||||
## Compiling
|
## Compiling
|
||||||
|
|
|
@ -25,17 +25,19 @@
|
||||||
|
|
||||||
package org.geysermc.platform.spigot.world;
|
package org.geysermc.platform.spigot.world;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
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.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_13_1to1_13.Protocol1_13_1To1_13;
|
||||||
import us.myles.ViaVersion.protocols.protocol1_16_2to1_16_1.data.MappingData;
|
import us.myles.ViaVersion.protocols.protocol1_16_2to1_16_1.data.MappingData;
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class GeyserSpigotWorldManager extends WorldManager {
|
public class GeyserSpigotWorldManager extends GeyserWorldManager {
|
||||||
|
|
||||||
private final boolean isLegacy;
|
private final boolean isLegacy;
|
||||||
// You need ViaVersion to connect to an older server with Geyser.
|
// You need ViaVersion to connect to an older server with Geyser.
|
||||||
|
@ -70,4 +72,19 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
||||||
return BlockTranslator.AIR;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,6 +149,11 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration {
|
||||||
return node.getNode("cache-chunks").getBoolean(false);
|
return node.getNode("cache-chunks").getBoolean(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCacheImages() {
|
||||||
|
return node.getNode("cache-skins").getInt(0);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAboveBedrockNetherBuilding() {
|
public boolean isAboveBedrockNetherBuilding() {
|
||||||
return node.getNode("above-bedrock-nether-building").getBoolean(false);
|
return node.getNode("above-bedrock-nether-building").getBoolean(false);
|
||||||
|
|
|
@ -96,6 +96,12 @@
|
||||||
<version>8.3.1</version>
|
<version>8.3.1</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.nukkitx.fastutil</groupId>
|
||||||
|
<artifactId>fastutil-object-object-maps</artifactId>
|
||||||
|
<version>8.3.1</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
|
|
|
@ -36,6 +36,9 @@ public interface GeyserLogger {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs a severe message and an exception to console
|
* Logs a severe message and an exception to console
|
||||||
|
*
|
||||||
|
* @param message the message to log
|
||||||
|
* @param error the error to throw
|
||||||
*/
|
*/
|
||||||
void severe(String message, Throwable error);
|
void severe(String message, Throwable error);
|
||||||
|
|
||||||
|
@ -48,6 +51,9 @@ public interface GeyserLogger {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs an error message and an exception to console
|
* Logs an error message and an exception to console
|
||||||
|
*
|
||||||
|
* @param message the message to log
|
||||||
|
* @param error the error to throw
|
||||||
*/
|
*/
|
||||||
void error(String message, Throwable error);
|
void error(String message, Throwable error);
|
||||||
|
|
||||||
|
|
|
@ -30,14 +30,14 @@ import org.geysermc.connector.ping.IGeyserPingPassthrough;
|
||||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.connector.GeyserLogger;
|
import org.geysermc.connector.GeyserLogger;
|
||||||
import org.geysermc.connector.command.CommandManager;
|
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 org.geysermc.connector.network.translators.world.WorldManager;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public interface GeyserBootstrap {
|
public interface GeyserBootstrap {
|
||||||
|
|
||||||
CachedChunkManager DEFAULT_CHUNK_MANAGER = new CachedChunkManager();
|
GeyserWorldManager DEFAULT_CHUNK_MANAGER = new GeyserWorldManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the GeyserBootstrap is enabled
|
* Called when the GeyserBootstrap is enabled
|
||||||
|
|
|
@ -77,6 +77,8 @@ public interface GeyserConfiguration {
|
||||||
|
|
||||||
boolean isCacheChunks();
|
boolean isCacheChunks();
|
||||||
|
|
||||||
|
int getCacheImages();
|
||||||
|
|
||||||
IMetricsInfo getMetrics();
|
IMetricsInfo getMetrics();
|
||||||
|
|
||||||
interface IBedrockConfiguration {
|
interface IBedrockConfiguration {
|
||||||
|
|
|
@ -87,6 +87,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||||
@JsonProperty("cache-chunks")
|
@JsonProperty("cache-chunks")
|
||||||
private boolean cacheChunks;
|
private boolean cacheChunks;
|
||||||
|
|
||||||
|
@JsonProperty("cache-images")
|
||||||
|
private int cacheImages = 0;
|
||||||
|
|
||||||
@JsonProperty("above-bedrock-nether-building")
|
@JsonProperty("above-bedrock-nether-building")
|
||||||
private boolean aboveBedrockNetherBuilding;
|
private boolean aboveBedrockNetherBuilding;
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat
|
||||||
import com.github.steveice10.mc.protocol.data.message.TextMessage;
|
import com.github.steveice10.mc.protocol.data.message.TextMessage;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
import com.nukkitx.math.vector.Vector3f;
|
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.AttributeData;
|
||||||
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
||||||
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
||||||
|
@ -38,7 +39,6 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
|
||||||
import org.geysermc.connector.entity.attribute.Attribute;
|
import org.geysermc.connector.entity.attribute.Attribute;
|
||||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
|
@ -47,12 +47,8 @@ import org.geysermc.connector.network.session.cache.EntityEffectCache;
|
||||||
import org.geysermc.connector.scoreboard.Team;
|
import org.geysermc.connector.scoreboard.Team;
|
||||||
import org.geysermc.connector.utils.AttributeUtils;
|
import org.geysermc.connector.utils.AttributeUtils;
|
||||||
import org.geysermc.connector.utils.MessageUtils;
|
import org.geysermc.connector.utils.MessageUtils;
|
||||||
import org.geysermc.connector.utils.SkinUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Getter @Setter
|
@Getter @Setter
|
||||||
|
@ -61,7 +57,7 @@ public class PlayerEntity extends LivingEntity {
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
private String username;
|
private String username;
|
||||||
private long lastSkinUpdate = -1;
|
private long lastSkinUpdate = -1;
|
||||||
private boolean playerList = true;
|
private boolean playerList = true; // Player is in the player list
|
||||||
private final EntityEffectCache effectCache;
|
private final EntityEffectCache effectCache;
|
||||||
|
|
||||||
private Entity leftParrot;
|
private Entity leftParrot;
|
||||||
|
@ -97,7 +93,7 @@ public class PlayerEntity extends LivingEntity {
|
||||||
addPlayerPacket.setMotion(motion);
|
addPlayerPacket.setMotion(motion);
|
||||||
addPlayerPacket.setHand(hand);
|
addPlayerPacket.setHand(hand);
|
||||||
addPlayerPacket.getAdventureSettings().setCommandPermission(CommandPermission.NORMAL);
|
addPlayerPacket.getAdventureSettings().setCommandPermission(CommandPermission.NORMAL);
|
||||||
addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.VISITOR);
|
addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.MEMBER);
|
||||||
addPlayerPacket.setDeviceId("");
|
addPlayerPacket.setDeviceId("");
|
||||||
addPlayerPacket.setPlatformChatId("");
|
addPlayerPacket.setPlatformChatId("");
|
||||||
addPlayerPacket.getMetadata().putAll(metadata);
|
addPlayerPacket.getMetadata().putAll(metadata);
|
||||||
|
@ -117,30 +113,12 @@ public class PlayerEntity extends LivingEntity {
|
||||||
public void sendPlayer(GeyserSession session) {
|
public void sendPlayer(GeyserSession session) {
|
||||||
if(session.getEntityCache().getPlayerEntity(uuid) == null)
|
if(session.getEntityCache().getPlayerEntity(uuid) == null)
|
||||||
return;
|
return;
|
||||||
if (getLastSkinUpdate() == -1) {
|
|
||||||
if (playerList) {
|
|
||||||
PlayerListPacket playerList = new PlayerListPacket();
|
|
||||||
playerList.setAction(PlayerListPacket.Action.ADD);
|
|
||||||
playerList.getEntries().add(SkinUtils.buildDefaultEntry(profile, geyserId));
|
|
||||||
session.sendUpstreamPacket(playerList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (session.getUpstream().isInitialized() && session.getEntityCache().getEntityByGeyserId(geyserId) == null) {
|
if (session.getUpstream().isInitialized() && session.getEntityCache().getEntityByGeyserId(geyserId) == null) {
|
||||||
session.getEntityCache().spawnEntity(this);
|
session.getEntityCache().spawnEntity(this);
|
||||||
} else {
|
} else {
|
||||||
spawnEntity(session);
|
spawnEntity(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!playerList) {
|
|
||||||
// remove from playerlist if player isn't on playerlist
|
|
||||||
GeyserConnector.getInstance().getGeneralThreadPool().execute(() -> {
|
|
||||||
PlayerListPacket playerList = new PlayerListPacket();
|
|
||||||
playerList.setAction(PlayerListPacket.Action.REMOVE);
|
|
||||||
playerList.getEntries().add(new PlayerListPacket.Entry(uuid));
|
|
||||||
session.sendUpstreamPacket(playerList);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -232,7 +210,7 @@ public class PlayerEntity extends LivingEntity {
|
||||||
|
|
||||||
if (entityMetadata.getId() == 2) {
|
if (entityMetadata.getId() == 2) {
|
||||||
// System.out.println(session.getScoreboardCache().getScoreboard().getObjectives().keySet());
|
// 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 name " + team.getName());
|
||||||
// session.getConnector().getLogger().info("team entities " + team.getEntities());
|
// session.getConnector().getLogger().info("team entities " + team.getEntities());
|
||||||
}
|
}
|
||||||
|
@ -241,7 +219,7 @@ public class PlayerEntity extends LivingEntity {
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
username = MessageUtils.getBedrockMessage(name);
|
username = MessageUtils.getBedrockMessage(name);
|
||||||
}
|
}
|
||||||
Team team = session.getScoreboardCache().getScoreboard().getTeamFor(username);
|
Team team = session.getWorldCache().getScoreboard().getTeamFor(username);
|
||||||
if (team != null) {
|
if (team != null) {
|
||||||
// session.getConnector().getLogger().info("team name es " + team.getName() + " with prefix " + team.getPrefix() + " and suffix " + team.getSuffix());
|
// 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());
|
metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix());
|
||||||
|
|
|
@ -53,14 +53,21 @@ public class ShulkerEntity extends GolemEntity {
|
||||||
metadata.put(EntityData.SHULKER_ATTACH_POS, Vector3i.from(position.getX(), position.getY(), position.getZ()));
|
metadata.put(EntityData.SHULKER_ATTACH_POS, Vector3i.from(position.getX(), position.getY(), position.getZ()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO Outdated metadata flag SHULKER_PEAK_HEIGHT
|
|
||||||
// if (entityMetadata.getId() == 17) {
|
if (entityMetadata.getId() == 17) {
|
||||||
// int height = (byte) entityMetadata.getValue();
|
int height = (byte) entityMetadata.getValue();
|
||||||
// metadata.put(EntityData.SHULKER_PEAK_HEIGHT, height);
|
metadata.put(EntityData.SHULKER_PEEK_ID, height);
|
||||||
// }
|
}
|
||||||
|
|
||||||
if (entityMetadata.getId() == 18) {
|
if (entityMetadata.getId() == 18) {
|
||||||
int color = Math.abs((byte) entityMetadata.getValue() - 15);
|
byte color = (byte) entityMetadata.getValue();
|
||||||
metadata.put(EntityData.VARIANT, color);
|
if (color == 16) {
|
||||||
|
// 16 is default on both editions
|
||||||
|
metadata.put(EntityData.VARIANT, 16);
|
||||||
|
} else {
|
||||||
|
// Every other shulker color is offset 15 in bedrock edition
|
||||||
|
metadata.put(EntityData.VARIANT, Math.abs(color - 15));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
super.updateBedrockMetadata(entityMetadata, session);
|
super.updateBedrockMetadata(entityMetadata, session);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
||||||
import org.geysermc.connector.utils.LoginEncryptionUtils;
|
import org.geysermc.connector.utils.LoginEncryptionUtils;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
|
import org.geysermc.connector.utils.SettingsUtils;
|
||||||
|
|
||||||
public class UpstreamPacketHandler extends LoggingPacketHandler {
|
public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
|
|
||||||
|
@ -91,6 +92,10 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(ModalFormResponsePacket packet) {
|
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());
|
return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormId(), packet.getFormData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ import com.nukkitx.math.vector.*;
|
||||||
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
||||||
import com.nukkitx.protocol.bedrock.BedrockServerSession;
|
import com.nukkitx.protocol.bedrock.BedrockServerSession;
|
||||||
import com.nukkitx.protocol.bedrock.data.*;
|
import com.nukkitx.protocol.bedrock.data.*;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
||||||
|
@ -54,6 +55,7 @@ import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.geysermc.common.window.CustomFormWindow;
|
||||||
import org.geysermc.common.window.FormWindow;
|
import org.geysermc.common.window.FormWindow;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.command.CommandSender;
|
import org.geysermc.connector.command.CommandSender;
|
||||||
|
@ -79,9 +81,7 @@ import java.net.InetSocketAddress;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -102,7 +102,7 @@ public class GeyserSession implements CommandSender {
|
||||||
private ChunkCache chunkCache;
|
private ChunkCache chunkCache;
|
||||||
private EntityCache entityCache;
|
private EntityCache entityCache;
|
||||||
private InventoryCache inventoryCache;
|
private InventoryCache inventoryCache;
|
||||||
private ScoreboardCache scoreboardCache;
|
private WorldCache worldCache;
|
||||||
private WindowCache windowCache;
|
private WindowCache windowCache;
|
||||||
@Setter
|
@Setter
|
||||||
private TeleportCache teleportCache;
|
private TeleportCache teleportCache;
|
||||||
|
@ -191,6 +191,41 @@ public class GeyserSession implements CommandSender {
|
||||||
|
|
||||||
private MinecraftProtocol protocol;
|
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) {
|
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
this.upstream = new UpstreamSession(bedrockServerSession);
|
this.upstream = new UpstreamSession(bedrockServerSession);
|
||||||
|
@ -198,7 +233,7 @@ public class GeyserSession implements CommandSender {
|
||||||
this.chunkCache = new ChunkCache(this);
|
this.chunkCache = new ChunkCache(this);
|
||||||
this.entityCache = new EntityCache(this);
|
this.entityCache = new EntityCache(this);
|
||||||
this.inventoryCache = new InventoryCache(this);
|
this.inventoryCache = new InventoryCache(this);
|
||||||
this.scoreboardCache = new ScoreboardCache(this);
|
this.worldCache = new WorldCache(this);
|
||||||
this.windowCache = new WindowCache(this);
|
this.windowCache = new WindowCache(this);
|
||||||
|
|
||||||
this.playerEntity = new PlayerEntity(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO);
|
this.playerEntity = new PlayerEntity(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO);
|
||||||
|
@ -249,17 +284,12 @@ public class GeyserSession implements CommandSender {
|
||||||
attributes.add(new AttributeData("minecraft:movement", 0.0f, 1024f, 0.1f, 0.1f));
|
attributes.add(new AttributeData("minecraft:movement", 0.0f, 1024f, 0.1f, 0.1f));
|
||||||
attributesPacket.setAttributes(attributes);
|
attributesPacket.setAttributes(attributes);
|
||||||
upstream.sendPacket(attributesPacket);
|
upstream.sendPacket(attributesPacket);
|
||||||
}
|
|
||||||
|
|
||||||
public void fetchOurSkin(PlayerListPacket.Entry entry) {
|
// Only allow the server to send health information
|
||||||
PlayerSkinPacket playerSkinPacket = new PlayerSkinPacket();
|
// Setting this to false allows natural regeneration to work false but doesn't break it being true
|
||||||
playerSkinPacket.setUuid(authData.getUUID());
|
GameRulesChangedPacket gamerulePacket = new GameRulesChangedPacket();
|
||||||
playerSkinPacket.setSkin(entry.getSkin());
|
gamerulePacket.getGameRules().add(new GameRuleData<>("naturalregeneration", false));
|
||||||
playerSkinPacket.setOldSkinName("OldName");
|
upstream.sendPacket(gamerulePacket);
|
||||||
playerSkinPacket.setNewSkinName("NewName");
|
|
||||||
playerSkinPacket.setTrustedSkin(true);
|
|
||||||
upstream.sendPacket(playerSkinPacket);
|
|
||||||
getConnector().getLogger().debug("Sending skin for " + playerEntity.getUsername() + " " + authData.getUUID());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void login() {
|
public void login() {
|
||||||
|
@ -445,7 +475,7 @@ public class GeyserSession implements CommandSender {
|
||||||
|
|
||||||
this.chunkCache = null;
|
this.chunkCache = null;
|
||||||
this.entityCache = null;
|
this.entityCache = null;
|
||||||
this.scoreboardCache = null;
|
this.worldCache = null;
|
||||||
this.inventoryCache = null;
|
this.inventoryCache = null;
|
||||||
this.windowCache = null;
|
this.windowCache = null;
|
||||||
|
|
||||||
|
@ -610,4 +640,69 @@ public class GeyserSession implements CommandSender {
|
||||||
connector.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server");
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given session's player has a permission
|
||||||
|
*
|
||||||
|
* @param permission The permission node to check
|
||||||
|
* @return true if the player has the requested permission, false if not
|
||||||
|
*/
|
||||||
|
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<AdventureSetting> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,9 @@
|
||||||
|
|
||||||
package org.geysermc.connector.network.session.cache;
|
package org.geysermc.connector.network.session.cache;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.scoreboard.Objective;
|
import org.geysermc.connector.scoreboard.Objective;
|
||||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||||
|
@ -33,11 +35,18 @@ import org.geysermc.connector.scoreboard.Scoreboard;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class ScoreboardCache {
|
public class WorldCache {
|
||||||
|
|
||||||
private GeyserSession session;
|
private GeyserSession session;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
private Difficulty difficulty = Difficulty.EASY;
|
||||||
|
|
||||||
|
private boolean showCoordinates = true;
|
||||||
|
|
||||||
private Scoreboard scoreboard;
|
private Scoreboard scoreboard;
|
||||||
|
|
||||||
public ScoreboardCache(GeyserSession session) {
|
public WorldCache(GeyserSession session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.scoreboard = new Scoreboard(session);
|
this.scoreboard = new Scoreboard(session);
|
||||||
}
|
}
|
||||||
|
@ -52,4 +61,14 @@ public class ScoreboardCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -23,15 +23,25 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.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<ServerSettingsRequestPacket> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
public void translate(ServerSettingsRequestPacket packet, GeyserSession session) {
|
||||||
return session.getChunkCache().getBlockAt(new Position(x, y, z));
|
SettingsUtils.buildForm(session);
|
||||||
|
|
||||||
|
ServerSettingsResponsePacket serverSettingsResponsePacket = new ServerSettingsResponsePacket();
|
||||||
|
serverSettingsResponsePacket.setFormData(session.getSettingsForm().getJSONData());
|
||||||
|
serverSettingsResponsePacket.setFormId(SettingsUtils.SETTINGS_FORM_ID);
|
||||||
|
session.sendUpstreamPacket(serverSettingsResponsePacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -44,8 +44,8 @@ public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslat
|
||||||
|
|
||||||
for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) {
|
for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) {
|
||||||
if (!entity.isValid()) {
|
if (!entity.isValid()) {
|
||||||
// async skin loading
|
SkinUtils.requestAndHandleSkinAndCape(entity, session, null);
|
||||||
SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session));
|
entity.sendPlayer(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.VillagerTrade;
|
||||||
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
import com.github.steveice10.mc.protocol.data.game.window.WindowType;
|
|
@ -23,7 +23,7 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.metadata.Position;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
|
||||||
|
@ -116,6 +116,18 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||||
// Handled in BedrockInventoryTransactionTranslator
|
// Handled in BedrockInventoryTransactionTranslator
|
||||||
break;
|
break;
|
||||||
case START_BREAK:
|
case START_BREAK:
|
||||||
|
if (session.getConnector().getConfig().isCacheChunks()) {
|
||||||
|
if (packet.getFace() == BlockFace.UP.ordinal()) {
|
||||||
|
int blockUp = session.getConnector().getWorldManager().getBlockAt(session, packet.getBlockPosition().add(0, 1, 0));
|
||||||
|
String identifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockUp);
|
||||||
|
if (identifier.startsWith("minecraft:fire") || identifier.startsWith("minecraft:soul_fire")) {
|
||||||
|
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(packet.getBlockPosition().getX(),
|
||||||
|
packet.getBlockPosition().getY() + 1, packet.getBlockPosition().getZ()), BlockFace.values()[packet.getFace()]);
|
||||||
|
session.sendDownstreamPacket(startBreakingPacket);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(packet.getBlockPosition().getX(),
|
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(packet.getBlockPosition().getX(),
|
||||||
packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), BlockFace.values()[packet.getFace()]);
|
packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), BlockFace.values()[packet.getFace()]);
|
||||||
session.sendDownstreamPacket(startBreakingPacket);
|
session.sendDownstreamPacket(startBreakingPacket);
|
|
@ -23,7 +23,7 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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 com.nukkitx.protocol.bedrock.packet.EmotePacket;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
|
@ -23,7 +23,7 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap;
|
|
@ -23,7 +23,7 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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 com.nukkitx.math.vector.Vector3d;
|
||||||
import org.geysermc.connector.common.ChatColor;
|
import org.geysermc.connector.common.ChatColor;
|
|
@ -23,7 +23,7 @@
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
* @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.data.SoundEvent;
|
||||||
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
|
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
|
|
@ -214,13 +214,6 @@ public class ItemRegistry {
|
||||||
return itemEntry;
|
return itemEntry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If item find was unsuccessful first time, we try again while ignoring damage
|
|
||||||
// Fixes piston, sticky pistons, dispensers and droppers turning into air from creative inventory
|
|
||||||
for (ItemEntry itemEntry : ITEM_ENTRIES.values()) {
|
|
||||||
if (itemEntry.getBedrockId() == data.getId()) {
|
|
||||||
return itemEntry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will hide the message when the player clicks with an empty hand
|
// This will hide the message when the player clicks with an empty hand
|
||||||
if (data.getId() != 0 && data.getDamage() != 0) {
|
if (data.getId() != 0 && data.getDamage() != 0) {
|
||||||
|
|
|
@ -40,5 +40,7 @@ public class JavaDifficultyTranslator extends PacketTranslator<ServerDifficultyP
|
||||||
SetDifficultyPacket setDifficultyPacket = new SetDifficultyPacket();
|
SetDifficultyPacket setDifficultyPacket = new SetDifficultyPacket();
|
||||||
setDifficultyPacket.setDifficulty(packet.getDifficulty().ordinal());
|
setDifficultyPacket.setDifficulty(packet.getDifficulty().ordinal());
|
||||||
session.sendUpstreamPacket(setDifficultyPacket);
|
session.sendUpstreamPacket(setDifficultyPacket);
|
||||||
|
|
||||||
|
session.getWorldCache().setDifficulty(packet.getDifficulty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class JavaJoinGameTranslator extends PacketTranslator<ServerJoinGamePacke
|
||||||
DimensionUtils.switchDimension(session, fakeDim);
|
DimensionUtils.switchDimension(session, fakeDim);
|
||||||
DimensionUtils.switchDimension(session, newDimension);
|
DimensionUtils.switchDimension(session, newDimension);
|
||||||
|
|
||||||
session.getScoreboardCache().removeScoreboard();
|
session.getWorldCache().removeScoreboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket();
|
AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket();
|
||||||
|
|
|
@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
||||||
import org.geysermc.connector.entity.Entity;
|
import org.geysermc.connector.entity.Entity;
|
||||||
|
import org.geysermc.connector.entity.PlayerEntity;
|
||||||
import org.geysermc.connector.entity.type.EntityType;
|
import org.geysermc.connector.entity.type.EntityType;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
|
@ -51,6 +52,33 @@ public class JavaEntityStatusTranslator extends PacketTranslator<ServerEntitySta
|
||||||
EntityEventPacket entityEventPacket = new EntityEventPacket();
|
EntityEventPacket entityEventPacket = new EntityEventPacket();
|
||||||
entityEventPacket.setRuntimeEntityId(entity.getGeyserId());
|
entityEventPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||||
switch (packet.getStatus()) {
|
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
|
// EntityEventType.HURT sends extra data depending on the type of damage. However this appears to have no visual changes
|
||||||
case LIVING_BURN:
|
case LIVING_BURN:
|
||||||
case LIVING_DROWN:
|
case LIVING_DROWN:
|
||||||
|
|
|
@ -32,6 +32,7 @@ import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
||||||
import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket;
|
import com.nukkitx.protocol.bedrock.packet.AdventureSettingsPacket;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
import org.geysermc.connector.entity.Entity;
|
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.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
@ -43,24 +44,12 @@ public class JavaPlayerAbilitiesTranslator extends PacketTranslator<ServerPlayer
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerPlayerAbilitiesPacket packet, GeyserSession session) {
|
public void translate(ServerPlayerAbilitiesPacket packet, GeyserSession session) {
|
||||||
Entity entity = session.getPlayerEntity();
|
PlayerEntity entity = session.getPlayerEntity();
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Set<AdventureSetting> playerFlags = new ObjectOpenHashSet<>();
|
session.setCanFly(packet.isCanFly());
|
||||||
playerFlags.add(AdventureSetting.AUTO_JUMP);
|
session.setFlying(packet.isFlying());
|
||||||
if (packet.isCanFly())
|
session.sendAdventureSettings();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,18 +82,7 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator<ServerPlayer
|
||||||
playerEntity.setPlayerList(true);
|
playerEntity.setPlayerList(true);
|
||||||
playerEntity.setValid(true);
|
playerEntity.setValid(true);
|
||||||
|
|
||||||
PlayerListPacket.Entry playerListEntry = SkinUtils.buildCachedEntry(entry.getProfile(), playerEntity.getGeyserId());
|
PlayerListPacket.Entry playerListEntry = SkinUtils.buildCachedEntry(session, entry.getProfile(), playerEntity.getGeyserId());
|
||||||
if (self) {
|
|
||||||
// Copy the entry with our identity instead.
|
|
||||||
PlayerListPacket.Entry copy = new PlayerListPacket.Entry(session.getAuthData().getUUID());
|
|
||||||
copy.setName(playerListEntry.getName());
|
|
||||||
copy.setEntityId(playerListEntry.getEntityId());
|
|
||||||
copy.setSkin(playerListEntry.getSkin());
|
|
||||||
copy.setXuid(playerListEntry.getXuid());
|
|
||||||
copy.setPlatformChatId(playerListEntry.getPlatformChatId());
|
|
||||||
copy.setTeacher(playerListEntry.isTeacher());
|
|
||||||
playerListEntry = copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
translate.getEntries().add(playerListEntry);
|
translate.getEntries().add(playerListEntry);
|
||||||
break;
|
break;
|
||||||
|
@ -103,15 +92,20 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator<ServerPlayer
|
||||||
// remove from tablist but player entity is still there
|
// remove from tablist but player entity is still there
|
||||||
entity.setPlayerList(false);
|
entity.setPlayerList(false);
|
||||||
} else {
|
} else {
|
||||||
// just remove it from caching
|
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
|
// just remove it from caching
|
||||||
session.getEntityCache().removePlayerEntity(entry.getProfile().getId());
|
session.getEntityCache().removePlayerEntity(entry.getProfile().getId());
|
||||||
} else {
|
} else {
|
||||||
entity.setPlayerList(false);
|
entity.setPlayerList(false);
|
||||||
session.getEntityCache().removeEntity(entity, false);
|
session.getEntityCache().removeEntity(entity, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
translate.getEntries().add(new PlayerListPacket.Entry(entry.getProfile().getId()));
|
if (entity == session.getPlayerEntity()) {
|
||||||
|
// If removing ourself we use our AuthData UUID
|
||||||
|
translate.getEntries().add(new PlayerListPacket.Entry(session.getAuthData().getUUID()));
|
||||||
|
} else {
|
||||||
|
translate.getEntries().add(new PlayerListPacket.Entry(entry.getProfile().getId()));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,9 +54,9 @@ public class JavaSpawnPlayerTranslator extends PacketTranslator<ServerSpawnPlaye
|
||||||
entity.setRotation(rotation);
|
entity.setRotation(rotation);
|
||||||
session.getEntityCache().cacheEntity(entity);
|
session.getEntityCache().cacheEntity(entity);
|
||||||
|
|
||||||
// async skin loading
|
|
||||||
if (session.getUpstream().isInitialized()) {
|
if (session.getUpstream().isInitialized()) {
|
||||||
SkinUtils.requestAndHandleSkinAndCape(entity, session, skinAndCape -> entity.sendPlayer(session));
|
entity.sendPlayer(session);
|
||||||
|
SkinUtils.requestAndHandleSkinAndCape(entity, session, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class JavaDisplayScoreboardTranslator extends PacketTranslator<ServerDisp
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerDisplayScoreboardPacket packet, GeyserSession session) {
|
public void translate(ServerDisplayScoreboardPacket packet, GeyserSession session) {
|
||||||
session.getScoreboardCache().getScoreboard().registerNewObjective(
|
session.getWorldCache().getScoreboard().registerNewObjective(
|
||||||
packet.getName(), packet.getPosition()
|
packet.getName(), packet.getPosition()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
package org.geysermc.connector.network.translators.java.scoreboard;
|
package org.geysermc.connector.network.translators.java.scoreboard;
|
||||||
|
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.session.cache.ScoreboardCache;
|
import org.geysermc.connector.network.session.cache.WorldCache;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
import org.geysermc.connector.scoreboard.Objective;
|
import org.geysermc.connector.scoreboard.Objective;
|
||||||
|
@ -41,7 +41,7 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerSc
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerScoreboardObjectivePacket packet, GeyserSession session) {
|
public void translate(ServerScoreboardObjectivePacket packet, GeyserSession session) {
|
||||||
ScoreboardCache cache = session.getScoreboardCache();
|
WorldCache cache = session.getWorldCache();
|
||||||
Scoreboard scoreboard = cache.getScoreboard();
|
Scoreboard scoreboard = cache.getScoreboard();
|
||||||
|
|
||||||
Objective objective = scoreboard.getObjective(packet.getName());
|
Objective objective = scoreboard.getObjective(packet.getName());
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||||
public void translate(ServerTeamPacket packet, GeyserSession session) {
|
public void translate(ServerTeamPacket packet, GeyserSession session) {
|
||||||
GeyserConnector.getInstance().getLogger().debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers()));
|
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());
|
Team team = scoreboard.getTeam(packet.getTeamName());
|
||||||
switch (packet.getAction()) {
|
switch (packet.getAction()) {
|
||||||
case CREATE:
|
case CREATE:
|
||||||
|
@ -65,21 +65,21 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||||
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode()))
|
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode()))
|
||||||
.setUpdateType(UpdateType.UPDATE);
|
.setUpdateType(UpdateType.UPDATE);
|
||||||
} else {
|
} else {
|
||||||
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ADD_PLAYER:
|
case ADD_PLAYER:
|
||||||
if(team != null){
|
if (team != null) {
|
||||||
team.addEntities(packet.getPlayers());
|
team.addEntities(packet.getPlayers());
|
||||||
} else {
|
} else {
|
||||||
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REMOVE_PLAYER:
|
case REMOVE_PLAYER:
|
||||||
if(team != null){
|
if (team != null) {
|
||||||
team.removeEntities(packet.getPlayers());
|
team.removeEntities(packet.getPlayers());
|
||||||
} else {
|
} else {
|
||||||
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REMOVE:
|
case REMOVE:
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class JavaUpdateScoreTranslator extends PacketTranslator<ServerUpdateScor
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerUpdateScorePacket packet, GeyserSession session) {
|
public void translate(ServerUpdateScorePacket packet, GeyserSession session) {
|
||||||
try {
|
try {
|
||||||
Scoreboard scoreboard = session.getScoreboardCache().getScoreboard();
|
Scoreboard scoreboard = session.getWorldCache().getScoreboard();
|
||||||
|
|
||||||
Objective objective = scoreboard.getObjective(packet.getObjective());
|
Objective objective = scoreboard.getObjective(packet.getObjective());
|
||||||
if (objective == null && packet.getAction() != ScoreboardAction.REMOVE) {
|
if (objective == null && packet.getAction() != ScoreboardAction.REMOVE) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityEventType;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
import org.geysermc.connector.entity.Entity;
|
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.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
@ -55,7 +56,7 @@ public class JavaNotifyClientTranslator extends PacketTranslator<ServerNotifyCli
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerNotifyClientPacket packet, GeyserSession session) {
|
public void translate(ServerNotifyClientPacket packet, GeyserSession session) {
|
||||||
Entity entity = session.getPlayerEntity();
|
PlayerEntity entity = session.getPlayerEntity();
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -75,39 +76,17 @@ public class JavaNotifyClientTranslator extends PacketTranslator<ServerNotifyCli
|
||||||
session.sendUpstreamPacket(stopRainPacket);
|
session.sendUpstreamPacket(stopRainPacket);
|
||||||
break;
|
break;
|
||||||
case CHANGE_GAMEMODE:
|
case CHANGE_GAMEMODE:
|
||||||
Set<AdventureSetting> playerFlags = new ObjectOpenHashSet<>();
|
|
||||||
GameMode gameMode = (GameMode) packet.getValue();
|
GameMode gameMode = (GameMode) packet.getValue();
|
||||||
if (gameMode == GameMode.ADVENTURE)
|
|
||||||
playerFlags.add(AdventureSetting.WORLD_IMMUTABLE);
|
|
||||||
|
|
||||||
if (gameMode == GameMode.CREATIVE)
|
session.setNoClip(gameMode == GameMode.SPECTATOR);
|
||||||
playerFlags.add(AdventureSetting.MAY_FLY);
|
session.setWorldImmutable(gameMode == GameMode.ADVENTURE || gameMode == GameMode.SPECTATOR);
|
||||||
|
session.sendAdventureSettings();
|
||||||
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);
|
|
||||||
|
|
||||||
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
|
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
|
||||||
playerGameTypePacket.setGamemode(gameMode.ordinal());
|
playerGameTypePacket.setGamemode(gameMode.ordinal());
|
||||||
session.sendUpstreamPacket(playerGameTypePacket);
|
session.sendUpstreamPacket(playerGameTypePacket);
|
||||||
session.setGameMode(gameMode);
|
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
|
// Update the crafting grid to add/remove barriers for creative inventory
|
||||||
PlayerInventoryTranslator.updateCraftingGrid(session, session.getInventory());
|
PlayerInventoryTranslator.updateCraftingGrid(session, session.getInventory());
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator<ServerSpawnPar
|
||||||
int r = (int) (data.getRed()*255);
|
int r = (int) (data.getRed()*255);
|
||||||
int g = (int) (data.getGreen()*255);
|
int g = (int) (data.getGreen()*255);
|
||||||
int b = (int) (data.getBlue()*255);
|
int b = (int) (data.getBlue()*255);
|
||||||
particle.setType(LevelEventType.PARTICLE_FALLING_DUST);
|
particle.setType(LevelEventType.PARTICLE_REDSTONE);
|
||||||
particle.setData(((0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff));
|
particle.setData(((0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff));
|
||||||
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
particle.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
|
||||||
session.sendUpstreamPacket(particle);
|
session.sendUpstreamPacket(particle);
|
||||||
|
|
|
@ -67,9 +67,7 @@ public class JavaUpdateTimeTranslator extends PacketTranslator<ServerUpdateTimeP
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDoDaylightCycleGamerule(GeyserSession session, boolean doCycle) {
|
private void setDoDaylightCycleGamerule(GeyserSession session, boolean doCycle) {
|
||||||
GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket();
|
session.sendGameRule("dodaylightcycle", doCycle);
|
||||||
gameRulesChangedPacket.getGameRules().add(new GameRuleData<>("dodaylightcycle", doCycle));
|
|
||||||
session.sendUpstreamPacket(gameRulesChangedPacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<String, String> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,8 +26,11 @@
|
||||||
package org.geysermc.connector.network.translators.world;
|
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.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 com.nukkitx.math.vector.Vector3i;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
|
import org.geysermc.connector.utils.GameRule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that manages or retrieves various information
|
* Class that manages or retrieves various information
|
||||||
|
@ -70,4 +73,56 @@ public abstract class WorldManager {
|
||||||
* @return the block state at the specified location
|
* @return the block state at the specified location
|
||||||
*/
|
*/
|
||||||
public abstract int getBlockAt(GeyserSession session, int x, int y, int z);
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ public class FileUtils {
|
||||||
*
|
*
|
||||||
* @param src File to load
|
* @param src File to load
|
||||||
* @param valueType Class to load file into
|
* @param valueType Class to load file into
|
||||||
|
* @param <T> the type
|
||||||
* @return The data as the given class
|
* @return The data as the given class
|
||||||
* @throws IOException if the config could not be loaded
|
* @throws IOException if the config could not be loaded
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -136,6 +136,9 @@ public class InventoryUtils {
|
||||||
/**
|
/**
|
||||||
* Returns a barrier block with custom name and lore to explain why
|
* Returns a barrier block with custom name and lore to explain why
|
||||||
* part of the inventory is unusable.
|
* part of the inventory is unusable.
|
||||||
|
*
|
||||||
|
* @param description the description
|
||||||
|
* @return the unusable space block
|
||||||
*/
|
*/
|
||||||
public static ItemData createUnusableSpaceBlock(String description) {
|
public static ItemData createUnusableSpaceBlock(String description) {
|
||||||
NbtMapBuilder root = NbtMap.builder();
|
NbtMapBuilder root = NbtMap.builder();
|
||||||
|
|
|
@ -122,7 +122,7 @@ public class LanguageUtils {
|
||||||
formatString = key;
|
formatString = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MessageFormat.format(formatString.replace("&", "\u00a7"), values);
|
return MessageFormat.format(formatString.replace("'", "''").replace("&", "\u00a7"), values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -95,7 +95,7 @@ public class MessageUtils {
|
||||||
* @param messages A {@link List} of {@link Message} to parse
|
* @param messages A {@link List} of {@link Message} to parse
|
||||||
* @param locale A locale loaded to get the message for
|
* @param locale A locale loaded to get the message for
|
||||||
* @param parent A {@link Message} to use as the parent (can be null)
|
* @param parent A {@link Message} to use as the parent (can be null)
|
||||||
* @return
|
* @return the translation parameters
|
||||||
*/
|
*/
|
||||||
public static List<String> getTranslationParams(List<Message> messages, String locale, Message parent) {
|
public static List<String> getTranslationParams(List<Message> messages, String locale, Message parent) {
|
||||||
List<String> strings = new ArrayList<>();
|
List<String> strings = new ArrayList<>();
|
||||||
|
@ -160,10 +160,10 @@ public class MessageUtils {
|
||||||
* Translate a given {@link TranslationMessage} to the given locale
|
* Translate a given {@link TranslationMessage} to the given locale
|
||||||
*
|
*
|
||||||
* @param message The {@link Message} to send
|
* @param message The {@link Message} to send
|
||||||
* @param locale
|
* @param locale the locale
|
||||||
* @param shouldTranslate
|
* @param shouldTranslate if the message should be translated
|
||||||
* @param parent
|
* @param parent the parent message
|
||||||
* @return
|
* @return the given translation message translated from the given locale
|
||||||
*/
|
*/
|
||||||
public static String getTranslatedBedrockMessage(Message message, String locale, boolean shouldTranslate, Message parent) {
|
public static String getTranslatedBedrockMessage(Message message, String locale, boolean shouldTranslate, Message parent) {
|
||||||
JsonParser parser = new JsonParser();
|
JsonParser parser = new JsonParser();
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,8 @@ package org.geysermc.connector.utils;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
@ -40,9 +42,11 @@ import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
@ -54,22 +58,26 @@ public class SkinProvider {
|
||||||
public static final Skin EMPTY_SKIN = new Skin(-1, "steve", STEVE_SKIN);
|
public static final Skin EMPTY_SKIN = new Skin(-1, "steve", STEVE_SKIN);
|
||||||
public static final byte[] ALEX_SKIN = new ProvidedSkin("bedrock/skin/skin_alex.png").getSkin();
|
public static final byte[] ALEX_SKIN = new ProvidedSkin("bedrock/skin/skin_alex.png").getSkin();
|
||||||
public static final Skin EMPTY_SKIN_ALEX = new Skin(-1, "alex", ALEX_SKIN);
|
public static final Skin EMPTY_SKIN_ALEX = new Skin(-1, "alex", ALEX_SKIN);
|
||||||
private static Map<UUID, Skin> cachedSkins = new ConcurrentHashMap<>();
|
private static final Cache<String, Skin> cachedSkins = CacheBuilder.newBuilder()
|
||||||
private static Map<UUID, CompletableFuture<Skin>> requestedSkins = new ConcurrentHashMap<>();
|
.expireAfterAccess(1, TimeUnit.HOURS)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private static final Map<String, CompletableFuture<Skin>> requestedSkins = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public static final Cape EMPTY_CAPE = new Cape("", "no-cape", new byte[0], -1, true);
|
public static final Cape EMPTY_CAPE = new Cape("", "no-cape", new byte[0], -1, true);
|
||||||
private static Map<String, Cape> cachedCapes = new ConcurrentHashMap<>();
|
private static final Cache<String, Cape> cachedCapes = CacheBuilder.newBuilder()
|
||||||
private static Map<String, CompletableFuture<Cape>> requestedCapes = new ConcurrentHashMap<>();
|
.expireAfterAccess(1, TimeUnit.HOURS)
|
||||||
|
.build();
|
||||||
|
private static final Map<String, CompletableFuture<Cape>> requestedCapes = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public static final SkinGeometry EMPTY_GEOMETRY = SkinProvider.SkinGeometry.getLegacy(false);
|
public static final SkinGeometry EMPTY_GEOMETRY = SkinProvider.SkinGeometry.getLegacy(false);
|
||||||
private static Map<UUID, SkinGeometry> cachedGeometry = new ConcurrentHashMap<>();
|
private static final Map<UUID, SkinGeometry> cachedGeometry = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public static final boolean ALLOW_THIRD_PARTY_EARS = GeyserConnector.getInstance().getConfig().isAllowThirdPartyEars();
|
public static final boolean ALLOW_THIRD_PARTY_EARS = GeyserConnector.getInstance().getConfig().isAllowThirdPartyEars();
|
||||||
public static String EARS_GEOMETRY;
|
public static String EARS_GEOMETRY;
|
||||||
public static String EARS_GEOMETRY_SLIM;
|
public static String EARS_GEOMETRY_SLIM;
|
||||||
|
|
||||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||||
private static final int CACHE_INTERVAL = 8 * 60 * 1000; // 8 minutes
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
/* Load in the normal ears geometry */
|
/* Load in the normal ears geometry */
|
||||||
|
@ -102,22 +110,44 @@ public class SkinProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
EARS_GEOMETRY_SLIM = earsDataBuilder.toString();
|
EARS_GEOMETRY_SLIM = earsDataBuilder.toString();
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean hasSkinCached(UUID uuid) {
|
// Schedule Daily Image Expiry if we are caching them
|
||||||
return cachedSkins.containsKey(uuid);
|
if (GeyserConnector.getInstance().getConfig().getCacheImages() > 0) {
|
||||||
|
GeyserConnector.getInstance().getGeneralThreadPool().scheduleAtFixedRate(() -> {
|
||||||
|
File cacheFolder = Paths.get("cache", "images").toFile();
|
||||||
|
if (!cacheFolder.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
final long expireTime = ((long)GeyserConnector.getInstance().getConfig().getCacheImages()) * ((long)1000 * 60 * 60 * 24);
|
||||||
|
for (File imageFile : Objects.requireNonNull(cacheFolder.listFiles())) {
|
||||||
|
if (imageFile.lastModified() < System.currentTimeMillis() - expireTime) {
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
imageFile.delete();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0) {
|
||||||
|
GeyserConnector.getInstance().getLogger().debug(String.format("Removed %d cached image files as they have expired", count));
|
||||||
|
}
|
||||||
|
}, 10, 1440, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean hasCapeCached(String capeUrl) {
|
public static boolean hasCapeCached(String capeUrl) {
|
||||||
return cachedCapes.containsKey(capeUrl);
|
return cachedCapes.getIfPresent(capeUrl) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Skin getCachedSkin(UUID uuid) {
|
public static Skin getCachedSkin(String skinUrl) {
|
||||||
return cachedSkins.getOrDefault(uuid, EMPTY_SKIN);
|
Skin skin = cachedSkins.getIfPresent(skinUrl);
|
||||||
|
return skin != null ? skin : EMPTY_SKIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Cape getCachedCape(String capeUrl) {
|
public static Cape getCachedCape(String capeUrl) {
|
||||||
return capeUrl != null ? cachedCapes.getOrDefault(capeUrl, EMPTY_CAPE) : EMPTY_CAPE;
|
Cape cape = capeUrl != null ? cachedCapes.getIfPresent(capeUrl) : EMPTY_CAPE;
|
||||||
|
return cape != null ? cape : EMPTY_CAPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompletableFuture<SkinAndCape> requestSkinAndCape(UUID playerId, String skinUrl, String capeUrl) {
|
public static CompletableFuture<SkinAndCape> requestSkinAndCape(UUID playerId, String skinUrl, String capeUrl) {
|
||||||
|
@ -137,28 +167,26 @@ public class SkinProvider {
|
||||||
|
|
||||||
public static CompletableFuture<Skin> requestSkin(UUID playerId, String textureUrl, boolean newThread) {
|
public static CompletableFuture<Skin> requestSkin(UUID playerId, String textureUrl, boolean newThread) {
|
||||||
if (textureUrl == null || textureUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_SKIN);
|
if (textureUrl == null || textureUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_SKIN);
|
||||||
if (requestedSkins.containsKey(playerId)) return requestedSkins.get(playerId); // already requested
|
if (requestedSkins.containsKey(textureUrl)) return requestedSkins.get(textureUrl); // already requested
|
||||||
|
|
||||||
if ((System.currentTimeMillis() - CACHE_INTERVAL) < cachedSkins.getOrDefault(playerId, EMPTY_SKIN).getRequestedOn()) {
|
Skin cachedSkin = cachedSkins.getIfPresent(textureUrl);
|
||||||
// no need to update, still cached
|
if (cachedSkin != null) {
|
||||||
return CompletableFuture.completedFuture(cachedSkins.get(playerId));
|
return CompletableFuture.completedFuture(cachedSkin);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture<Skin> future;
|
CompletableFuture<Skin> future;
|
||||||
if (newThread) {
|
if (newThread) {
|
||||||
future = CompletableFuture.supplyAsync(() -> supplySkin(playerId, textureUrl), EXECUTOR_SERVICE)
|
future = CompletableFuture.supplyAsync(() -> supplySkin(playerId, textureUrl), EXECUTOR_SERVICE)
|
||||||
.whenCompleteAsync((skin, throwable) -> {
|
.whenCompleteAsync((skin, throwable) -> {
|
||||||
if (!cachedSkins.getOrDefault(playerId, EMPTY_SKIN).getTextureUrl().equals(textureUrl)) {
|
skin.updated = true;
|
||||||
skin.updated = true;
|
cachedSkins.put(textureUrl, skin);
|
||||||
cachedSkins.put(playerId, skin);
|
requestedSkins.remove(textureUrl);
|
||||||
}
|
|
||||||
requestedSkins.remove(skin.getSkinOwner());
|
|
||||||
});
|
});
|
||||||
requestedSkins.put(playerId, future);
|
requestedSkins.put(textureUrl, future);
|
||||||
} else {
|
} else {
|
||||||
Skin skin = supplySkin(playerId, textureUrl);
|
Skin skin = supplySkin(playerId, textureUrl);
|
||||||
future = CompletableFuture.completedFuture(skin);
|
future = CompletableFuture.completedFuture(skin);
|
||||||
cachedSkins.put(playerId, skin);
|
cachedSkins.put(textureUrl, skin);
|
||||||
}
|
}
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
@ -168,11 +196,9 @@ public class SkinProvider {
|
||||||
if (requestedCapes.containsKey(capeUrl)) return requestedCapes.get(capeUrl); // already requested
|
if (requestedCapes.containsKey(capeUrl)) return requestedCapes.get(capeUrl); // already requested
|
||||||
|
|
||||||
boolean officialCape = provider == CapeProvider.MINECRAFT;
|
boolean officialCape = provider == CapeProvider.MINECRAFT;
|
||||||
boolean validCache = (System.currentTimeMillis() - CACHE_INTERVAL) < cachedCapes.getOrDefault(capeUrl, EMPTY_CAPE).getRequestedOn();
|
Cape cachedCape = cachedCapes.getIfPresent(capeUrl);
|
||||||
|
if (cachedCape != null) {
|
||||||
if ((cachedCapes.containsKey(capeUrl) && officialCape) || validCache) {
|
return CompletableFuture.completedFuture(cachedCape);
|
||||||
// the cape is an official cape (static) or the cape doesn't need a update yet
|
|
||||||
return CompletableFuture.completedFuture(cachedCapes.get(capeUrl));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletableFuture<Cape> future;
|
CompletableFuture<Cape> future;
|
||||||
|
@ -245,7 +271,10 @@ public class SkinProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompletableFuture<Cape> requestBedrockCape(UUID playerID, boolean newThread) {
|
public static CompletableFuture<Cape> requestBedrockCape(UUID playerID, boolean newThread) {
|
||||||
Cape bedrockCape = cachedCapes.getOrDefault(playerID.toString() + ".Bedrock", EMPTY_CAPE);
|
Cape bedrockCape = cachedCapes.getIfPresent(playerID.toString() + ".Bedrock");
|
||||||
|
if (bedrockCape == null) {
|
||||||
|
bedrockCape = EMPTY_CAPE;
|
||||||
|
}
|
||||||
return CompletableFuture.completedFuture(bedrockCape);
|
return CompletableFuture.completedFuture(bedrockCape);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,7 +285,7 @@ public class SkinProvider {
|
||||||
|
|
||||||
public static void storeBedrockSkin(UUID playerID, String skinID, byte[] skinData) {
|
public static void storeBedrockSkin(UUID playerID, String skinID, byte[] skinData) {
|
||||||
Skin skin = new Skin(playerID, skinID, skinData, System.currentTimeMillis(), true, false);
|
Skin skin = new Skin(playerID, skinID, skinData, System.currentTimeMillis(), true, false);
|
||||||
cachedSkins.put(playerID, skin);
|
cachedSkins.put(skin.getTextureUrl(), skin);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void storeBedrockCape(UUID playerID, byte[] capeData) {
|
public static void storeBedrockCape(UUID playerID, byte[] capeData) {
|
||||||
|
@ -276,7 +305,7 @@ public class SkinProvider {
|
||||||
* @param skin The skin to cache
|
* @param skin The skin to cache
|
||||||
*/
|
*/
|
||||||
public static void storeEarSkin(UUID playerID, Skin skin) {
|
public static void storeEarSkin(UUID playerID, Skin skin) {
|
||||||
cachedSkins.put(playerID, skin);
|
cachedSkins.put(skin.getTextureUrl(), skin);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -290,11 +319,12 @@ public class SkinProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Skin supplySkin(UUID uuid, String textureUrl) {
|
private static Skin supplySkin(UUID uuid, String textureUrl) {
|
||||||
byte[] skin = EMPTY_SKIN.getSkinData();
|
|
||||||
try {
|
try {
|
||||||
skin = requestImage(textureUrl, null);
|
byte[] skin = requestImage(textureUrl, null);
|
||||||
|
return new Skin(uuid, textureUrl, skin, System.currentTimeMillis(), false, false);
|
||||||
} catch (Exception ignored) {} // just ignore I guess
|
} catch (Exception ignored) {} // just ignore I guess
|
||||||
return new Skin(uuid, textureUrl, skin, System.currentTimeMillis(), false, false);
|
|
||||||
|
return new Skin(uuid, "empty", EMPTY_SKIN.getSkinData(), System.currentTimeMillis(), false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Cape supplyCape(String capeUrl, CapeProvider provider) {
|
private static Cape supplyCape(String capeUrl, CapeProvider provider) {
|
||||||
|
@ -356,11 +386,38 @@ public class SkinProvider {
|
||||||
return existingSkin;
|
return existingSkin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
private static byte[] requestImage(String imageUrl, CapeProvider provider) throws Exception {
|
private static byte[] requestImage(String imageUrl, CapeProvider provider) throws Exception {
|
||||||
BufferedImage image = downloadImage(imageUrl, provider);
|
BufferedImage image = null;
|
||||||
GeyserConnector.getInstance().getLogger().debug("Downloaded " + imageUrl);
|
|
||||||
|
|
||||||
// if the requested image is an cape
|
// First see if we have a cached file. We also update the modification stamp so we know when the file was last used
|
||||||
|
File imageFile = Paths.get("cache", "images", UUID.nameUUIDFromBytes(imageUrl.getBytes()).toString() + ".png").toFile();
|
||||||
|
if (imageFile.exists()) {
|
||||||
|
try {
|
||||||
|
GeyserConnector.getInstance().getLogger().debug("Reading cached image from file " + imageFile.getPath() + " for " + imageUrl);
|
||||||
|
imageFile.setLastModified(System.currentTimeMillis());
|
||||||
|
image = ImageIO.read(imageFile);
|
||||||
|
} catch (IOException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no image we download it
|
||||||
|
if (image == null) {
|
||||||
|
image = downloadImage(imageUrl, provider);
|
||||||
|
GeyserConnector.getInstance().getLogger().debug("Downloaded " + imageUrl);
|
||||||
|
|
||||||
|
// Write to cache if we are allowed
|
||||||
|
if (GeyserConnector.getInstance().getConfig().getCacheImages() > 0) {
|
||||||
|
imageFile.getParentFile().mkdirs();
|
||||||
|
try {
|
||||||
|
ImageIO.write(image, "png", imageFile);
|
||||||
|
GeyserConnector.getInstance().getLogger().debug("Writing cached skin to file " + imageFile.getPath() + " for " + imageUrl);
|
||||||
|
} catch (IOException e) {
|
||||||
|
GeyserConnector.getInstance().getLogger().error("Failed to write cached skin to file " + imageFile.getPath() + " for " + imageUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the requested image is a cape
|
||||||
if (provider != null) {
|
if (provider != null) {
|
||||||
while(image.getWidth() > 64) {
|
while(image.getWidth() > 64) {
|
||||||
image = scale(image);
|
image = scale(image);
|
||||||
|
|
|
@ -35,6 +35,7 @@ import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.connector.common.AuthType;
|
import org.geysermc.connector.common.AuthType;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.entity.Entity;
|
||||||
import org.geysermc.connector.entity.PlayerEntity;
|
import org.geysermc.connector.entity.PlayerEntity;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.session.auth.BedrockClientData;
|
import org.geysermc.connector.network.session.auth.BedrockClientData;
|
||||||
|
@ -47,18 +48,21 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
public class SkinUtils {
|
public class SkinUtils {
|
||||||
|
|
||||||
public static PlayerListPacket.Entry buildCachedEntry(GameProfile profile, long geyserId) {
|
public static PlayerListPacket.Entry buildCachedEntry(GeyserSession session, GameProfile profile, long geyserId) {
|
||||||
GameProfileData data = GameProfileData.from(profile);
|
GameProfileData data = GameProfileData.from(profile);
|
||||||
SkinProvider.Cape cape = SkinProvider.getCachedCape(data.getCapeUrl());
|
SkinProvider.Cape cape = SkinProvider.getCachedCape(data.getCapeUrl());
|
||||||
|
|
||||||
SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex());
|
SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex());
|
||||||
|
|
||||||
|
SkinProvider.Skin skin = SkinProvider.getCachedSkin(data.getSkinUrl());
|
||||||
|
|
||||||
return buildEntryManually(
|
return buildEntryManually(
|
||||||
|
session,
|
||||||
profile.getId(),
|
profile.getId(),
|
||||||
profile.getName(),
|
profile.getName(),
|
||||||
geyserId,
|
geyserId,
|
||||||
profile.getIdAsString(),
|
skin.getTextureUrl(),
|
||||||
SkinProvider.getCachedSkin(profile.getId()).getSkinData(),
|
skin.getSkinData(),
|
||||||
cape.getCapeId(),
|
cape.getCapeId(),
|
||||||
cape.getCapeData(),
|
cape.getCapeData(),
|
||||||
geometry.getGeometryName(),
|
geometry.getGeometryName(),
|
||||||
|
@ -66,12 +70,13 @@ public class SkinUtils {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PlayerListPacket.Entry buildDefaultEntry(GameProfile profile, long geyserId) {
|
public static PlayerListPacket.Entry buildDefaultEntry(GeyserSession session, GameProfile profile, long geyserId) {
|
||||||
return buildEntryManually(
|
return buildEntryManually(
|
||||||
|
session,
|
||||||
profile.getId(),
|
profile.getId(),
|
||||||
profile.getName(),
|
profile.getName(),
|
||||||
geyserId,
|
geyserId,
|
||||||
profile.getIdAsString(),
|
"default",
|
||||||
SkinProvider.STEVE_SKIN,
|
SkinProvider.STEVE_SKIN,
|
||||||
SkinProvider.EMPTY_CAPE.getCapeId(),
|
SkinProvider.EMPTY_CAPE.getCapeId(),
|
||||||
SkinProvider.EMPTY_CAPE.getCapeData(),
|
SkinProvider.EMPTY_CAPE.getCapeData(),
|
||||||
|
@ -80,20 +85,38 @@ public class SkinUtils {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PlayerListPacket.Entry buildEntryManually(UUID uuid, String username, long geyserId,
|
public static PlayerListPacket.Entry buildEntryManually(GeyserSession session, UUID uuid, String username, long geyserId,
|
||||||
String skinId, byte[] skinData,
|
String skinId, byte[] skinData,
|
||||||
String capeId, byte[] capeData,
|
String capeId, byte[] capeData,
|
||||||
String geometryName, String geometryData) {
|
String geometryName, String geometryData) {
|
||||||
SerializedSkin serializedSkin = SerializedSkin.of(
|
SerializedSkin serializedSkin = SerializedSkin.of(
|
||||||
skinId, geometryName, ImageData.of(skinData), Collections.emptyList(),
|
skinId, geometryName, ImageData.of(skinData), Collections.emptyList(),
|
||||||
ImageData.of(capeData), geometryData, "", true, false, !capeId.equals(SkinProvider.EMPTY_CAPE.getCapeId()), capeId, uuid.toString()
|
ImageData.of(capeData), geometryData, "", true, false, !capeId.equals(SkinProvider.EMPTY_CAPE.getCapeId()), capeId, skinId
|
||||||
);
|
);
|
||||||
|
|
||||||
PlayerListPacket.Entry entry = new PlayerListPacket.Entry(uuid);
|
// 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
|
||||||
|
// as bedrock expects to get back its own provided uuid
|
||||||
|
if (session.getPlayerEntity().getUuid().equals(uuid)) {
|
||||||
|
entry = new PlayerListPacket.Entry(session.getAuthData().getUUID());
|
||||||
|
} else {
|
||||||
|
entry = new PlayerListPacket.Entry(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
entry.setName(username);
|
entry.setName(username);
|
||||||
entry.setEntityId(geyserId);
|
entry.setEntityId(geyserId);
|
||||||
entry.setSkin(serializedSkin);
|
entry.setSkin(serializedSkin);
|
||||||
entry.setXuid("");
|
entry.setXuid(xuid);
|
||||||
entry.setPlatformChatId("");
|
entry.setPlatformChatId("");
|
||||||
entry.setTeacher(false);
|
entry.setTeacher(false);
|
||||||
entry.setTrustedSkin(true);
|
entry.setTrustedSkin(true);
|
||||||
|
@ -201,48 +224,34 @@ public class SkinUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entity.getLastSkinUpdate() < skin.getRequestedOn()) {
|
entity.setLastSkinUpdate(skin.getRequestedOn());
|
||||||
entity.setLastSkinUpdate(skin.getRequestedOn());
|
|
||||||
|
|
||||||
if (session.getUpstream().isInitialized()) {
|
if (session.getUpstream().isInitialized()) {
|
||||||
PlayerListPacket.Entry updatedEntry = buildEntryManually(
|
PlayerListPacket.Entry updatedEntry = buildEntryManually(
|
||||||
entity.getUuid(),
|
session,
|
||||||
entity.getUsername(),
|
entity.getUuid(),
|
||||||
entity.getGeyserId(),
|
entity.getUsername(),
|
||||||
entity.getUuid().toString(),
|
entity.getGeyserId(),
|
||||||
skin.getSkinData(),
|
skin.getTextureUrl(),
|
||||||
cape.getCapeId(),
|
skin.getSkinData(),
|
||||||
cape.getCapeData(),
|
cape.getCapeId(),
|
||||||
geometry.getGeometryName(),
|
cape.getCapeData(),
|
||||||
geometry.getGeometryData()
|
geometry.getGeometryName(),
|
||||||
);
|
geometry.getGeometryData()
|
||||||
|
);
|
||||||
|
|
||||||
// If it is our skin we replace the UUID with the authdata UUID
|
|
||||||
if (session.getPlayerEntity() == entity) {
|
|
||||||
// Copy the entry with our identity instead.
|
|
||||||
PlayerListPacket.Entry copy = new PlayerListPacket.Entry(session.getAuthData().getUUID());
|
|
||||||
copy.setName(updatedEntry.getName());
|
|
||||||
copy.setEntityId(updatedEntry.getEntityId());
|
|
||||||
copy.setSkin(updatedEntry.getSkin());
|
|
||||||
copy.setXuid(updatedEntry.getXuid());
|
|
||||||
copy.setPlatformChatId(updatedEntry.getPlatformChatId());
|
|
||||||
copy.setTeacher(updatedEntry.isTeacher());
|
|
||||||
updatedEntry = copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
PlayerListPacket playerAddPacket = new PlayerListPacket();
|
||||||
|
playerAddPacket.setAction(PlayerListPacket.Action.ADD);
|
||||||
|
playerAddPacket.getEntries().add(updatedEntry);
|
||||||
|
session.sendUpstreamPacket(playerAddPacket);
|
||||||
|
|
||||||
|
if (!entity.isPlayerList()) {
|
||||||
PlayerListPacket playerRemovePacket = new PlayerListPacket();
|
PlayerListPacket playerRemovePacket = new PlayerListPacket();
|
||||||
playerRemovePacket.setAction(PlayerListPacket.Action.REMOVE);
|
playerRemovePacket.setAction(PlayerListPacket.Action.REMOVE);
|
||||||
playerRemovePacket.getEntries().add(updatedEntry);
|
playerRemovePacket.getEntries().add(updatedEntry);
|
||||||
session.sendUpstreamPacket(playerRemovePacket);
|
session.sendUpstreamPacket(playerRemovePacket);
|
||||||
|
|
||||||
PlayerListPacket playerAddPacket = new PlayerListPacket();
|
|
||||||
playerAddPacket.setAction(PlayerListPacket.Action.ADD);
|
|
||||||
playerAddPacket.getEntries().add(updatedEntry);
|
|
||||||
session.sendUpstreamPacket(playerAddPacket);
|
|
||||||
|
|
||||||
if(entity.getUuid().equals(session.getPlayerEntity().getUuid())) {
|
|
||||||
session.fetchOurSkin(updatedEntry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -94,6 +94,10 @@ show-cooldown: true
|
||||||
# Geyser has direct access to the server itself.
|
# Geyser has direct access to the server itself.
|
||||||
cache-chunks: false
|
cache-chunks: false
|
||||||
|
|
||||||
|
# Specify how many days images will be cached to disk to save downloading them from the internet.
|
||||||
|
# A value of 0 is disabled. (Default: 0)
|
||||||
|
cache-images: 0
|
||||||
|
|
||||||
# Bedrock prevents building and displaying blocks above Y127 in the Nether -
|
# Bedrock prevents building and displaying blocks above Y127 in the Nether -
|
||||||
# enabling this config option works around that by changing the Nether dimension ID
|
# enabling this config option works around that by changing the Nether dimension ID
|
||||||
# to the End ID. The main downside to this is that the sky will resemble that of
|
# to the End ID. The main downside to this is that the sky will resemble that of
|
||||||
|
|
Loading…
Reference in a new issue