mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-12-23 15:00:37 +01:00
Merge master into Spigot rename
This commit is contained in:
commit
427f4ef83d
40 changed files with 525 additions and 114 deletions
|
@ -1,4 +1,4 @@
|
|||
<img src="https://geysermc.org/img/geyserlogo.png" alt="Geyser" width="600"/>
|
||||
<img src="https://geysermc.org/img/geyser-1760-860.png" alt="Geyser" width="600"/>
|
||||
|
||||
[![forthebadge made-with-java](http://ForTheBadge.com/images/badges/made-with-java.svg)](https://java.com/)
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ import org.geysermc.platform.bungeecord.command.GeyserBungeeCommandManager;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
@ -134,4 +135,9 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
public IGeyserPingPassthrough getGeyserPingPassthrough() {
|
||||
return geyserBungeePingPassthrough;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getConfigFolder() {
|
||||
return getDataFolder().toPath();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ import us.myles.ViaVersion.api.Via;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
|
@ -162,6 +163,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
return this.geyserWorldManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getConfigFolder() {
|
||||
return getDataFolder().toPath();
|
||||
}
|
||||
|
||||
public boolean isCompatible(String version, String whichVersion) {
|
||||
int[] currentVersion = parseVersion(version);
|
||||
int[] otherVersion = parseVersion(whichVersion);
|
||||
|
@ -195,4 +201,5 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -129,6 +129,11 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration {
|
|||
return node.getNode("allow-third-party-ears").getBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowCooldown() {
|
||||
return node.getNode("show-cooldown").getBoolean(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultLocale() {
|
||||
return node.getNode("default-locale").getString("en_us");
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.spongepowered.api.plugin.Plugin;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
|
||||
@Plugin(id = "geyser", name = GeyserConnector.NAME + "-Sponge", version = GeyserConnector.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
|
||||
|
@ -147,6 +148,11 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
|
|||
return geyserSpongePingPassthrough;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getConfigFolder() {
|
||||
return configDir.toPath();
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onServerStart(GameStartedServerEvent event) {
|
||||
onEnable();
|
||||
|
|
|
@ -37,6 +37,8 @@ import org.geysermc.platform.standalone.command.GeyserCommandManager;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||
|
@ -100,4 +102,10 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||
public IGeyserPingPassthrough getGeyserPingPassthrough() {
|
||||
return geyserPingPassthrough;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getConfigFolder() {
|
||||
// Return the current working directory
|
||||
return Paths.get(System.getProperty("user.dir"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements org
|
|||
|
||||
@Override
|
||||
public void info(String message) {
|
||||
log.info(printConsole(ChatColor.WHITE + message, colored));
|
||||
log.info(printConsole(ChatColor.RESET + ChatColor.BOLD + message, colored));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -34,6 +34,7 @@ import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
|||
import com.velocitypowered.api.plugin.Plugin;
|
||||
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
|
@ -48,6 +49,8 @@ import org.slf4j.Logger;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.UUID;
|
||||
|
||||
@Plugin(id = "geyser", name = GeyserConnector.NAME + "-Velocity", version = GeyserConnector.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
|
||||
|
@ -69,14 +72,16 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||
|
||||
private GeyserConnector connector;
|
||||
|
||||
@Getter
|
||||
private final Path configFolder = Paths.get("plugins/" + GeyserConnector.NAME + "-Velocity/");
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
File configDir = new File("plugins/" + GeyserConnector.NAME + "-Velocity/");
|
||||
|
||||
try {
|
||||
if (!configDir.exists())
|
||||
configDir.mkdir();
|
||||
File configFile = FileUtils.fileOrCopiedFromResource(new File(configDir, "config.yml"), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()));
|
||||
if (!configFolder.toFile().exists())
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
configFolder.toFile().mkdirs();
|
||||
File configFile = FileUtils.fileOrCopiedFromResource(configFolder.resolve("config.yml").toFile(), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()));
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserVelocityConfiguration.class);
|
||||
} catch (IOException ex) {
|
||||
logger.warn("Failed to read/create config.yml! Make sure it's up to date and/or readable+writable!", ex);
|
||||
|
@ -101,7 +106,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||
return;
|
||||
}
|
||||
|
||||
geyserConfig.loadFloodgate(this, proxyServer, configDir);
|
||||
geyserConfig.loadFloodgate(this, proxyServer, configFolder.toFile());
|
||||
|
||||
this.connector = GeyserConnector.start(PlatformType.VELOCITY, this);
|
||||
|
||||
|
|
|
@ -32,14 +32,14 @@ public class FormImage {
|
|||
|
||||
@Getter
|
||||
@Setter
|
||||
private FormImageType type;
|
||||
private String type;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String data;
|
||||
|
||||
public FormImage(FormImageType type, String data) {
|
||||
this.type = type;
|
||||
this.type = type.getName();
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,9 +25,17 @@
|
|||
|
||||
package org.geysermc.common.window.component;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class ToggleComponent extends FormComponent {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String text;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean defaultValue;
|
||||
|
||||
public ToggleComponent(String text) {
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
|||
import com.nukkitx.protocol.bedrock.BedrockServer;
|
||||
import com.nukkitx.protocol.bedrock.v390.Bedrock_v390;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.common.AuthType;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||
|
@ -79,6 +80,7 @@ public class GeyserConnector {
|
|||
private static GeyserConnector instance;
|
||||
|
||||
private RemoteServer remoteServer;
|
||||
@Setter
|
||||
private AuthType authType;
|
||||
|
||||
private boolean shuttingDown = false;
|
||||
|
|
|
@ -33,6 +33,8 @@ import org.geysermc.connector.command.CommandManager;
|
|||
import org.geysermc.connector.network.translators.world.CachedChunkManager;
|
||||
import org.geysermc.connector.network.translators.world.WorldManager;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public interface GeyserBootstrap {
|
||||
|
||||
CachedChunkManager DEFAULT_CHUNK_MANAGER = new CachedChunkManager();
|
||||
|
@ -83,4 +85,11 @@ public interface GeyserBootstrap {
|
|||
default WorldManager getWorldManager() {
|
||||
return DEFAULT_CHUNK_MANAGER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data folder where files get stored
|
||||
*
|
||||
* @return Path location of data folder
|
||||
*/
|
||||
Path getConfigFolder();
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ public abstract class CommandManager {
|
|||
args = new String[0];
|
||||
} else {
|
||||
label = command.substring(0, command.indexOf(" ")).toLowerCase();
|
||||
String argLine = command.substring(command.indexOf(" " + 1));
|
||||
String argLine = command.substring(command.indexOf(" ") + 1);
|
||||
args = argLine.contains(" ") ? argLine.split(" ") : new String[] { argLine };
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@ public interface GeyserConfiguration {
|
|||
|
||||
boolean isAllowThirdPartyEars();
|
||||
|
||||
boolean isShowCooldown();
|
||||
|
||||
String getDefaultLocale();
|
||||
|
||||
Path getFloodgateKeyFile();
|
||||
|
|
|
@ -75,6 +75,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
@JsonProperty("allow-third-party-capes")
|
||||
private boolean allowThirdPartyCapes;
|
||||
|
||||
@JsonProperty("show-cooldown")
|
||||
private boolean showCooldown = true;
|
||||
|
||||
@JsonProperty("allow-third-party-ears")
|
||||
private boolean allowThirdPartyEars;
|
||||
|
||||
|
|
|
@ -44,19 +44,28 @@ public class BoatEntity extends Entity {
|
|||
private final float ROWING_SPEED = 0.05f;
|
||||
|
||||
public BoatEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position.add(0d, entityType.getOffset(), 0d), motion, rotation.add(0, 0, 90));
|
||||
super(entityId, geyserId, entityType, position.add(0d, entityType.getOffset(), 0d), motion, rotation.add(90, 0, 90));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
||||
// Rotation is basically only called when entering/exiting a boat.
|
||||
// We don't include the rotation (y) as it causes the boat to appear sideways
|
||||
super.moveAbsolute(session, position.add(0d, this.entityType.getOffset(), 0d), Vector3f.from(0, 0, rotation.getZ() + 90), isOnGround, teleported);
|
||||
super.moveAbsolute(session, position.add(0d, this.entityType.getOffset(), 0d), Vector3f.from(rotation.getX() + 90, 0, rotation.getX() + 90), isOnGround, teleported);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
|
||||
super.moveRelative(session, relX, relY, relZ, Vector3f.from(0, 0, rotation.getZ()), isOnGround);
|
||||
super.moveRelative(session, relX, relY, relZ, Vector3f.from(rotation.getX(), 0, rotation.getX()), isOnGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePositionAndRotation(GeyserSession session, double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) {
|
||||
moveRelative(session, moveX, moveY, moveZ, yaw + 90, pitch, isOnGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRotation(GeyserSession session, float yaw, float pitch, boolean isOnGround) {
|
||||
moveRelative(session, 0, 0, 0, Vector3f.from(yaw + 90, 0, 0), isOnGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -78,6 +78,11 @@ public class Entity {
|
|||
*/
|
||||
protected Vector3f rotation;
|
||||
|
||||
/**
|
||||
* Saves if the entity should be on the ground. Otherwise entities like parrots are flapping when rotating
|
||||
*/
|
||||
protected boolean onGround;
|
||||
|
||||
protected float scale = 1;
|
||||
|
||||
protected EntityType entityType;
|
||||
|
@ -150,11 +155,12 @@ public class Entity {
|
|||
}
|
||||
|
||||
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, float yaw, float pitch, boolean isOnGround) {
|
||||
moveRelative(session, relX, relY, relZ, Vector3f.from(yaw, pitch, yaw), isOnGround);
|
||||
moveRelative(session, relX, relY, relZ, Vector3f.from(yaw, pitch, this.rotation.getZ()), isOnGround);
|
||||
}
|
||||
|
||||
public void moveRelative(GeyserSession session, double relX, double relY, double relZ, Vector3f rotation, boolean isOnGround) {
|
||||
setRotation(rotation);
|
||||
setOnGround(isOnGround);
|
||||
this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
|
||||
|
||||
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
|
||||
|
@ -168,12 +174,13 @@ public class Entity {
|
|||
}
|
||||
|
||||
public void moveAbsolute(GeyserSession session, Vector3f position, float yaw, float pitch, boolean isOnGround, boolean teleported) {
|
||||
moveAbsolute(session, position, Vector3f.from(yaw, pitch, yaw), isOnGround, teleported);
|
||||
moveAbsolute(session, position, Vector3f.from(yaw, pitch, this.rotation.getZ()), isOnGround, teleported);
|
||||
}
|
||||
|
||||
public void moveAbsolute(GeyserSession session, Vector3f position, Vector3f rotation, boolean isOnGround, boolean teleported) {
|
||||
setPosition(position);
|
||||
setRotation(rotation);
|
||||
setOnGround(isOnGround);
|
||||
|
||||
MoveEntityAbsolutePacket moveEntityPacket = new MoveEntityAbsolutePacket();
|
||||
moveEntityPacket.setRuntimeEntityId(geyserId);
|
||||
|
@ -185,6 +192,52 @@ public class Entity {
|
|||
session.sendUpstreamPacket(moveEntityPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleports an entity to a new location. Used in JavaEntityTeleportTranslator.
|
||||
* @param session GeyserSession.
|
||||
* @param position The new position of the entity.
|
||||
* @param yaw The new yaw of the entity.
|
||||
* @param pitch The new pitch of the entity.
|
||||
* @param isOnGround Whether the entity is currently on the ground.
|
||||
*/
|
||||
public void teleport(GeyserSession session, Vector3f position, float yaw, float pitch, boolean isOnGround) {
|
||||
moveAbsolute(session, position, yaw, pitch, isOnGround, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an entity's head position. Used in JavaEntityHeadLookTranslator.
|
||||
* @param session GeyserSession.
|
||||
* @param headYaw The new head rotation of the entity.
|
||||
*/
|
||||
public void updateHeadLookRotation(GeyserSession session, float headYaw) {
|
||||
moveRelative(session, 0, 0, 0, Vector3f.from(headYaw, rotation.getY(), rotation.getZ()), onGround);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an entity's position and rotation. Used in JavaEntityPositionRotationTranslator.
|
||||
* @param session GeyserSession
|
||||
* @param moveX The new X offset of the current position.
|
||||
* @param moveY The new Y offset of the current position.
|
||||
* @param moveZ The new Z offset of the current position.
|
||||
* @param yaw The new yaw of the entity.
|
||||
* @param pitch The new pitch of the entity.
|
||||
* @param isOnGround Whether the entity is currently on the ground.
|
||||
*/
|
||||
public void updatePositionAndRotation(GeyserSession session, double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) {
|
||||
moveRelative(session, moveX, moveY, moveZ, Vector3f.from(rotation.getX(), pitch, yaw), isOnGround);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an entity's rotation. Used in JavaEntityRotationTranslator.
|
||||
* @param session GeyserSession.
|
||||
* @param yaw The new yaw of the entity.
|
||||
* @param pitch The new pitch of the entity.
|
||||
* @param isOnGround Whether the entity is currently on the ground.
|
||||
*/
|
||||
public void updateRotation(GeyserSession session, float yaw, float pitch, boolean isOnGround) {
|
||||
updatePositionAndRotation(session, 0, 0, 0, yaw, pitch, isOnGround);
|
||||
}
|
||||
|
||||
public void updateBedrockAttributes(GeyserSession session) {
|
||||
if (!valid) return;
|
||||
|
||||
|
|
|
@ -62,6 +62,11 @@ public class PaintingEntity extends Entity {
|
|||
session.getConnector().getLogger().debug("Spawned painting on " + position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHeadLookRotation(GeyserSession session, float headYaw) {
|
||||
// Do nothing, as head look messes up paintings
|
||||
}
|
||||
|
||||
public Vector3f fixOffset(boolean toBedrock) {
|
||||
if (toBedrock) {
|
||||
Vector3f position = super.position;
|
||||
|
|
|
@ -57,7 +57,6 @@ public class PlayerEntity extends LivingEntity {
|
|||
private String username;
|
||||
private long lastSkinUpdate = -1;
|
||||
private boolean playerList = true;
|
||||
private boolean onGround;
|
||||
private final EntityEffectCache effectCache;
|
||||
|
||||
private Entity leftParrot;
|
||||
|
@ -144,7 +143,7 @@ public class PlayerEntity extends LivingEntity {
|
|||
setPosition(position);
|
||||
setRotation(rotation);
|
||||
|
||||
this.onGround = isOnGround;
|
||||
setOnGround(isOnGround);
|
||||
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(geyserId);
|
||||
|
@ -171,7 +170,7 @@ public class PlayerEntity extends LivingEntity {
|
|||
setRotation(rotation);
|
||||
this.position = Vector3f.from(position.getX() + relX, position.getY() + relY, position.getZ() + relZ);
|
||||
|
||||
this.onGround = isOnGround;
|
||||
setOnGround(isOnGround);
|
||||
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(geyserId);
|
||||
|
@ -188,6 +187,35 @@ public class PlayerEntity extends LivingEntity {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateHeadLookRotation(GeyserSession session, float headYaw) {
|
||||
moveRelative(session, 0, 0, 0, Vector3f.from(rotation.getX(), rotation.getY(), headYaw), onGround);
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(geyserId);
|
||||
movePlayerPacket.setPosition(position);
|
||||
movePlayerPacket.setRotation(getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.ROTATION);
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePositionAndRotation(GeyserSession session, double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) {
|
||||
moveRelative(session, moveX, moveY, moveZ, yaw, pitch, isOnGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRotation(GeyserSession session, float yaw, float pitch, boolean isOnGround) {
|
||||
super.updateRotation(session, yaw, pitch, isOnGround);
|
||||
// Both packets need to be sent or else player head rotation isn't correctly updated
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(geyserId);
|
||||
movePlayerPacket.setPosition(position);
|
||||
movePlayerPacket.setRotation(getBedrockRotation());
|
||||
movePlayerPacket.setOnGround(isOnGround);
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.ROTATION);
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPosition(Vector3f position) {
|
||||
this.position = position.add(0, entityType.getOffset(), 0);
|
||||
|
@ -227,7 +255,7 @@ public class PlayerEntity extends LivingEntity {
|
|||
}
|
||||
|
||||
// Parrot occupying shoulder
|
||||
if (entityMetadata.getId() == 18 || entityMetadata.getId() == 19) {
|
||||
if ((entityMetadata.getId() == 18 && leftParrot == null) || (entityMetadata.getId() == 19 && rightParrot == null)) { // null check since this code just creates the parrot
|
||||
CompoundTag tag = (CompoundTag) entityMetadata.getValue();
|
||||
if (tag != null && !tag.isEmpty()) {
|
||||
// The parrot is a separate entity in Bedrock, but part of the player entity in Java
|
||||
|
|
|
@ -27,21 +27,58 @@ package org.geysermc.connector.entity.living.animal;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.Attribute;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PigEntity extends AnimalEntity {
|
||||
|
||||
// For updating the pig heart visual easier
|
||||
private float health = 20f;
|
||||
|
||||
public PigEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
if (entityMetadata.getId() == 8) {
|
||||
health = (float) entityMetadata.getValue();
|
||||
updateBedrockAttributes(session);
|
||||
}
|
||||
|
||||
if (entityMetadata.getId() == 16) {
|
||||
metadata.getFlags().setFlag(EntityFlag.SADDLED, (boolean) entityMetadata.getValue());
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockAttributes(GeyserSession session) {
|
||||
if (!valid) return;
|
||||
|
||||
float maxHealth = attributes.containsKey(AttributeType.MAX_HEALTH) ? attributes.get(AttributeType.MAX_HEALTH).getValue() : 20f;
|
||||
|
||||
List<Attribute> attributesLocal = new ArrayList<>();
|
||||
for (Map.Entry<AttributeType, org.geysermc.connector.entity.attribute.Attribute> entry : this.attributes.entrySet()) {
|
||||
if (!entry.getValue().getType().isBedrockAttribute())
|
||||
continue;
|
||||
|
||||
attributesLocal.add(AttributeUtils.getBedrockAttribute(entry.getValue()));
|
||||
}
|
||||
attributesLocal.add(new Attribute("minecraft:health", 0.0f, maxHealth, health, maxHealth));
|
||||
|
||||
UpdateAttributesPacket updateAttributesPacket = new UpdateAttributesPacket();
|
||||
updateAttributesPacket.setRuntimeEntityId(geyserId);
|
||||
updateAttributesPacket.setAttributes(attributesLocal);
|
||||
session.sendUpstreamPacket(updateAttributesPacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,11 @@ public class CatEntity extends TameableEntity {
|
|||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRotation(GeyserSession session, float yaw, float pitch, boolean isOnGround) {
|
||||
moveRelative(session, 0, 0, 0, Vector3f.from(this.rotation.getX(), pitch, yaw), isOnGround);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
if (entityMetadata.getId() == 18) {
|
||||
|
|
|
@ -28,10 +28,16 @@ package org.geysermc.connector.entity.living.merchant;
|
|||
import com.nukkitx.math.vector.Vector3f;
|
||||
import org.geysermc.connector.entity.living.AgeableEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
public class AbstractMerchantEntity extends AgeableEntity {
|
||||
|
||||
public AbstractMerchantEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport(GeyserSession session, Vector3f position, float yaw, float pitch, boolean isOnGround) {
|
||||
super.teleport(session, position, yaw - 180, pitch, isOnGround);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ import com.nukkitx.protocol.bedrock.BedrockPacket;
|
|||
import com.nukkitx.protocol.bedrock.BedrockServerSession;
|
||||
import com.nukkitx.protocol.bedrock.data.*;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||
import lombok.Getter;
|
||||
|
@ -57,7 +59,6 @@ import org.geysermc.connector.GeyserConnector;
|
|||
import org.geysermc.connector.command.CommandSender;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.inventory.PlayerInventory;
|
||||
import org.geysermc.connector.network.remote.RemoteServer;
|
||||
import org.geysermc.connector.network.session.auth.AuthData;
|
||||
|
@ -108,6 +109,9 @@ public class GeyserSession implements CommandSender {
|
|||
@Setter
|
||||
private TeleportCache teleportCache;
|
||||
|
||||
@Getter
|
||||
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
/**
|
||||
* A map of Vector3i positions to Java entity IDs.
|
||||
* Used for translating Bedrock block actions to Java entity actions.
|
||||
|
@ -175,6 +179,18 @@ public class GeyserSession implements CommandSender {
|
|||
@Setter
|
||||
private long lastInteractedVillagerEid;
|
||||
|
||||
/**
|
||||
* The current attack speed of the player. Used for sending proper cooldown timings.
|
||||
*/
|
||||
@Setter
|
||||
private double attackSpeed;
|
||||
/**
|
||||
* The time of the last hit. Used to gauge how long the cooldown is taking.
|
||||
* This is a session variable in order to prevent more scheduled threads than necessary.
|
||||
*/
|
||||
@Setter
|
||||
private long lastHitTime;
|
||||
|
||||
private MinecraftProtocol protocol;
|
||||
|
||||
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.connector.network.translators.bedrock;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityDataMap;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityFlag;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
@ -74,33 +75,43 @@ public class BedrockInteractTranslator extends PacketTranslator<InteractPacket>
|
|||
Entity interactEntity = session.getEntityCache().getEntityByGeyserId(packet.getRuntimeEntityId());
|
||||
if (interactEntity == null)
|
||||
return;
|
||||
EntityDataMap entityMetadata = interactEntity.getMetadata();
|
||||
|
||||
String interactiveTag;
|
||||
switch (interactEntity.getEntityType()) {
|
||||
case PIG:
|
||||
if (interactEntity.getMetadata().getFlags().getFlag(EntityFlag.SADDLED)) {
|
||||
interactiveTag = "action.interact.mount";
|
||||
} else interactiveTag = "";
|
||||
case BOAT:
|
||||
interactiveTag = "action.interact.ride.boat";
|
||||
break;
|
||||
case HORSE:
|
||||
case SKELETON_HORSE:
|
||||
case ZOMBIE_HORSE:
|
||||
case DONKEY:
|
||||
case MULE:
|
||||
case HORSE:
|
||||
case LLAMA:
|
||||
case MULE:
|
||||
case SKELETON_HORSE:
|
||||
case TRADER_LLAMA:
|
||||
if (interactEntity.getMetadata().getFlags().getFlag(EntityFlag.TAMED)) {
|
||||
case ZOMBIE_HORSE:
|
||||
if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) {
|
||||
interactiveTag = "action.interact.ride.horse";
|
||||
} else {
|
||||
interactiveTag = "action.interact.mount";
|
||||
}
|
||||
break;
|
||||
case BOAT:
|
||||
interactiveTag = "action.interact.ride.boat";
|
||||
break;
|
||||
case MINECART:
|
||||
interactiveTag = "action.interact.ride.minecart";
|
||||
break;
|
||||
case PIG:
|
||||
if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) {
|
||||
interactiveTag = "action.interact.mount";
|
||||
} else interactiveTag = "";
|
||||
break;
|
||||
case VILLAGER:
|
||||
if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0
|
||||
&& entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby
|
||||
interactiveTag = "action.interact.trade";
|
||||
} else interactiveTag = "";
|
||||
break;
|
||||
case WANDERING_TRADER:
|
||||
interactiveTag = "action.interact.trade"; // Since you can always trade with a wandering villager, presumably.
|
||||
break;
|
||||
default:
|
||||
return; // No need to process any further since there is no interactive tag
|
||||
}
|
||||
|
|
|
@ -25,10 +25,12 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.data.SoundEvent;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
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.CooldownUtils;
|
||||
|
||||
@Translator(packet = LevelSoundEventPacket.class)
|
||||
public class BedrockLevelSoundEventTranslator extends PacketTranslator<LevelSoundEventPacket> {
|
||||
|
@ -37,5 +39,12 @@ public class BedrockLevelSoundEventTranslator extends PacketTranslator<LevelSoun
|
|||
public void translate(LevelSoundEventPacket packet, GeyserSession session) {
|
||||
// lol what even :thinking:
|
||||
session.sendUpstreamPacket(packet);
|
||||
|
||||
// Yes, what even, but thankfully we can hijack this packet to send the cooldown
|
||||
if (packet.getSound() == SoundEvent.ATTACK_NODAMAGE || packet.getSound() == SoundEvent.ATTACK || packet.getSound() == SoundEvent.ATTACK_STRONG) {
|
||||
// Send a faux cooldown since Bedrock has no cooldown support
|
||||
// Sent here because Java still sends a cooldown if the player doesn't hit anything but Bedrock always sends a sound
|
||||
CooldownUtils.sendCooldown(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.bedrock;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.packet.MapInfoRequestPacket;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Translator(packet = MapInfoRequestPacket.class)
|
||||
public class BedrockMapInfoRequestTranslator extends PacketTranslator<MapInfoRequestPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(MapInfoRequestPacket packet, GeyserSession session) {
|
||||
long mapID = packet.getUniqueMapId();
|
||||
|
||||
if (session.getStoredMaps().containsKey(mapID)) {
|
||||
// Delay the packet 100ms to prevent the client from ignoring the packet
|
||||
GeyserConnector.getInstance().getGeneralThreadPool().schedule(() -> {
|
||||
session.sendUpstreamPacket(session.getStoredMaps().get(mapID));
|
||||
session.getStoredMaps().remove(mapID);
|
||||
}, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ import org.geysermc.connector.network.translators.Translator;
|
|||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerChangeHeldItemPacket;
|
||||
import com.nukkitx.protocol.bedrock.data.ContainerId;
|
||||
import com.nukkitx.protocol.bedrock.packet.MobEquipmentPacket;
|
||||
import org.geysermc.connector.utils.CooldownUtils;
|
||||
|
||||
@Translator(packet = MobEquipmentPacket.class)
|
||||
public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipmentPacket> {
|
||||
|
@ -47,5 +48,8 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
|||
|
||||
ClientPlayerChangeHeldItemPacket changeHeldItemPacket = new ClientPlayerChangeHeldItemPacket(packet.getHotbarSlot());
|
||||
session.sendDownstreamPacket(changeHeldItemPacket);
|
||||
|
||||
// Java sends a cooldown indicator whenever you switch an item
|
||||
CooldownUtils.sendCooldown(session);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ package org.geysermc.connector.network.translators.item;
|
|||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
|
||||
import com.github.steveice10.mc.protocol.data.message.Message;
|
||||
import com.nukkitx.nbt.CompoundTagBuilder;
|
||||
import com.github.steveice10.opennbt.tag.builtin.*;
|
||||
import com.nukkitx.nbt.CompoundTagBuilder;
|
||||
import com.nukkitx.nbt.tag.CompoundTag;
|
||||
import com.nukkitx.nbt.tag.Tag;
|
||||
import com.nukkitx.protocol.bedrock.data.ItemData;
|
||||
|
@ -41,11 +41,7 @@ import org.geysermc.connector.network.translators.ItemRemapper;
|
|||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class ItemTranslator {
|
||||
|
@ -126,12 +122,20 @@ public abstract class ItemTranslator {
|
|||
|
||||
ItemEntry bedrockItem = ItemRegistry.getItem(stack);
|
||||
|
||||
ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), stack.getNbt() != null ? stack.getNbt().clone() : null);
|
||||
com.github.steveice10.opennbt.tag.builtin.CompoundTag nbt = stack.getNbt() != null ? stack.getNbt().clone() : null;
|
||||
|
||||
if (itemStack.getNbt() != null) {
|
||||
// This is a fallback for maps with no nbt
|
||||
if (nbt == null && bedrockItem.getJavaIdentifier().equals("minecraft:filled_map")) {
|
||||
nbt = new com.github.steveice10.opennbt.tag.builtin.CompoundTag("");
|
||||
nbt.put(new IntTag("map", 0));
|
||||
}
|
||||
|
||||
ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), nbt);
|
||||
|
||||
if (nbt != null) {
|
||||
for (NbtItemStackTranslator translator : NBT_TRANSLATORS) {
|
||||
if (translator.acceptItem(bedrockItem)) {
|
||||
translator.translateToBedrock(itemStack.getNbt(), bedrockItem);
|
||||
translator.translateToBedrock(nbt, bedrockItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,4 +57,9 @@ public class MapItemTranslator extends NbtItemStackTranslator {
|
|||
itemTag.remove("map_uuid");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(ItemEntry itemEntry) {
|
||||
return itemEntry.getJavaIdentifier().equals("minecraft:filled_map");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,17 +25,12 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityHeadLookPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityHeadLookPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||
|
||||
@Translator(packet = ServerEntityHeadLookPacket.class)
|
||||
public class JavaEntityHeadLookTranslator extends PacketTranslator<ServerEntityHeadLookPacket> {
|
||||
|
||||
|
@ -48,21 +43,6 @@ public class JavaEntityHeadLookTranslator extends PacketTranslator<ServerEntityH
|
|||
|
||||
if (entity == null) return;
|
||||
|
||||
entity.setRotation(Vector3f.from(entity.getRotation().getX(), entity.getRotation().getY(), packet.getHeadYaw()));
|
||||
|
||||
if (entity.getEntityType() != EntityType.PLAYER && entity.getEntityType() != EntityType.PAINTING) {
|
||||
MoveEntityAbsolutePacket moveEntityAbsolutePacket = new MoveEntityAbsolutePacket();
|
||||
moveEntityAbsolutePacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
moveEntityAbsolutePacket.setPosition(entity.getPosition());
|
||||
moveEntityAbsolutePacket.setRotation(entity.getBedrockRotation());
|
||||
session.sendUpstreamPacket(moveEntityAbsolutePacket);
|
||||
} else {
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
movePlayerPacket.setPosition(entity.getPosition());
|
||||
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.ROTATION);
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
}
|
||||
entity.updateHeadLookRotation(session, packet.getHeadYaw());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,14 +25,12 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityPositionRotationPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityPositionRotationPacket;
|
||||
|
||||
@Translator(packet = ServerEntityPositionRotationPacket.class)
|
||||
public class JavaEntityPositionRotationTranslator extends PacketTranslator<ServerEntityPositionRotationPacket> {
|
||||
|
||||
|
@ -43,10 +41,7 @@ public class JavaEntityPositionRotationTranslator extends PacketTranslator<Serve
|
|||
entity = session.getPlayerEntity();
|
||||
}
|
||||
if (entity == null) return;
|
||||
if (entity.getEntityType() == EntityType.BOAT) {
|
||||
entity.moveRelative(session, packet.getMoveX(), packet.getMoveY(), packet.getMoveZ(), packet.getYaw() - 90, packet.getPitch(), packet.isOnGround());
|
||||
} else {
|
||||
entity.moveRelative(session, packet.getMoveX(), packet.getMoveY(), packet.getMoveZ(), packet.getYaw(), packet.getPitch(), packet.isOnGround());
|
||||
}
|
||||
|
||||
entity.updatePositionAndRotation(session, packet.getMoveX(), packet.getMoveY(), packet.getMoveZ(), packet.getYaw(), packet.getPitch(), packet.isOnGround());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityPropertiesPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
@ -32,17 +34,16 @@ import org.geysermc.connector.network.translators.PacketTranslator;
|
|||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityPropertiesPacket;
|
||||
|
||||
@Translator(packet = ServerEntityPropertiesPacket.class)
|
||||
public class JavaEntityPropertiesTranslator extends PacketTranslator<ServerEntityPropertiesPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerEntityPropertiesPacket packet, GeyserSession session) {
|
||||
boolean isSessionPlayer = false;
|
||||
Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
|
||||
if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
|
||||
entity = session.getPlayerEntity();
|
||||
isSessionPlayer = true;
|
||||
}
|
||||
if (entity == null) return;
|
||||
|
||||
|
@ -54,6 +55,13 @@ public class JavaEntityPropertiesTranslator extends PacketTranslator<ServerEntit
|
|||
case GENERIC_ATTACK_DAMAGE:
|
||||
entity.getAttributes().put(AttributeType.ATTACK_DAMAGE, AttributeType.ATTACK_DAMAGE.getAttribute((float) AttributeUtils.calculateValue(attribute)));
|
||||
break;
|
||||
case GENERIC_ATTACK_SPEED:
|
||||
if (isSessionPlayer) {
|
||||
// Get attack speed value for use in sending the faux cooldown
|
||||
double attackSpeed = AttributeUtils.calculateValue(attribute);
|
||||
session.setAttackSpeed(attackSpeed);
|
||||
}
|
||||
break;
|
||||
case GENERIC_FLYING_SPEED:
|
||||
entity.getAttributes().put(AttributeType.FLYING_SPEED, AttributeType.FLYING_SPEED.getAttribute((float) AttributeUtils.calculateValue(attribute)));
|
||||
entity.getAttributes().put(AttributeType.MOVEMENT_SPEED, AttributeType.MOVEMENT_SPEED.getAttribute((float) AttributeUtils.calculateValue(attribute)));
|
||||
|
|
|
@ -25,17 +25,12 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityRotationPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityRotationPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.packet.MoveEntityAbsolutePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||
|
||||
@Translator(packet = ServerEntityRotationPacket.class)
|
||||
public class JavaEntityRotationTranslator extends PacketTranslator<ServerEntityRotationPacket> {
|
||||
|
||||
|
@ -47,23 +42,6 @@ public class JavaEntityRotationTranslator extends PacketTranslator<ServerEntityR
|
|||
}
|
||||
if (entity == null) return;
|
||||
|
||||
// entity.moveRelative(packet.getMovementX(), packet.getMovementY(), packet.getMovementZ(), packet.getYaw(), packet.getPitch());
|
||||
entity.setRotation(Vector3f.from(packet.getYaw(), packet.getPitch(), packet.getYaw()));
|
||||
|
||||
if (entity.getEntityType() != EntityType.PLAYER) {
|
||||
MoveEntityAbsolutePacket moveEntityAbsolutePacket = new MoveEntityAbsolutePacket();
|
||||
moveEntityAbsolutePacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
moveEntityAbsolutePacket.setPosition(entity.getPosition());
|
||||
moveEntityAbsolutePacket.setRotation(entity.getBedrockRotation());
|
||||
session.sendUpstreamPacket(moveEntityAbsolutePacket);
|
||||
} else {
|
||||
MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
|
||||
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
movePlayerPacket.setPosition(entity.getPosition());
|
||||
movePlayerPacket.setRotation(entity.getBedrockRotation());
|
||||
movePlayerPacket.setOnGround(packet.isOnGround());
|
||||
movePlayerPacket.setMode(MovePlayerPacket.Mode.ROTATION);
|
||||
session.sendUpstreamPacket(movePlayerPacket);
|
||||
}
|
||||
entity.updateRotation(session, packet.getYaw(), packet.getPitch(), packet.isOnGround());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,6 @@ public class JavaEntityTeleportTranslator extends PacketTranslator<ServerEntityT
|
|||
}
|
||||
if (entity == null) return;
|
||||
|
||||
entity.moveAbsolute(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), packet.isOnGround(), false);
|
||||
entity.teleport(session, Vector3f.from(packet.getX(), packet.getY(), packet.getZ()), packet.getYaw(), packet.getPitch(), packet.isOnGround());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,10 +31,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.object.ProjectileData;
|
|||
import com.github.steveice10.mc.protocol.data.game.entity.type.EntityType;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.spawn.ServerSpawnEntityPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.FallingBlockEntity;
|
||||
import org.geysermc.connector.entity.FishingHookEntity;
|
||||
import org.geysermc.connector.entity.ItemFrameEntity;
|
||||
import org.geysermc.connector.entity.*;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
@ -73,6 +70,10 @@ public class JavaSpawnEntityTranslator extends PacketTranslator<ServerSpawnEntit
|
|||
// Fishing bobbers need the owner for the line
|
||||
entity = new FishingHookEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
type, position, motion, rotation, (ProjectileData) packet.getData());
|
||||
} else if (packet.getType() == EntityType.BOAT) {
|
||||
// Initial rotation is incorrect
|
||||
entity = new BoatEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
type, position, motion, Vector3f.from(packet.getYaw(), 0, packet.getYaw()));
|
||||
} else {
|
||||
Constructor<? extends Entity> entityConstructor = entityClass.getConstructor(long.class, long.class, org.geysermc.connector.entity.type.EntityType.class,
|
||||
Vector3f.class, Vector3f.class, Vector3f.class);
|
||||
|
|
|
@ -42,6 +42,7 @@ public class JavaMapDataTranslator extends PacketTranslator<ServerMapDataPacket>
|
|||
@Override
|
||||
public void translate(ServerMapDataPacket packet, GeyserSession session) {
|
||||
ClientboundMapItemDataPacket mapItemDataPacket = new ClientboundMapItemDataPacket();
|
||||
boolean shouldStore = false;
|
||||
|
||||
mapItemDataPacket.setUniqueMapId(packet.getMapId());
|
||||
mapItemDataPacket.setDimensionId(session.getPlayerEntity().getDimension());
|
||||
|
@ -55,6 +56,11 @@ public class JavaMapDataTranslator extends PacketTranslator<ServerMapDataPacket>
|
|||
mapItemDataPacket.setWidth(data.getColumns());
|
||||
mapItemDataPacket.setHeight(data.getRows());
|
||||
|
||||
// We have a full map image, this usually only happens on spawn for the initial image
|
||||
if (mapItemDataPacket.getWidth() == 128 && mapItemDataPacket.getHeight() == 128) {
|
||||
shouldStore = true;
|
||||
}
|
||||
|
||||
// Every int entry is an ABGR color
|
||||
int[] colors = new int[data.getData().length];
|
||||
|
||||
|
@ -76,6 +82,12 @@ public class JavaMapDataTranslator extends PacketTranslator<ServerMapDataPacket>
|
|||
id++;
|
||||
}
|
||||
|
||||
session.getUpstream().getSession().sendPacket(mapItemDataPacket);
|
||||
// Store the map to send when the client requests it, as bedrock expects the data after a MapInfoRequestPacket
|
||||
if (shouldStore) {
|
||||
session.getStoredMaps().put(mapItemDataPacket.getUniqueMapId(), mapItemDataPacket);
|
||||
}
|
||||
|
||||
// Send anyway just in case
|
||||
session.sendUpstreamPacket(mapItemDataPacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,12 @@ public class AttributeUtils {
|
|||
return new com.nukkitx.protocol.bedrock.data.Attribute(type.getBedrockIdentifier(), attribute.getMinimum(), attribute.getMaximum(), attribute.getValue(), attribute.getDefaultValue());
|
||||
}
|
||||
|
||||
//https://minecraft.gamepedia.com/Attribute#Modifiers
|
||||
/**
|
||||
* Retrieve the base attribute value with all modifiers applied.
|
||||
* https://minecraft.gamepedia.com/Attribute#Modifiers
|
||||
* @param attribute The attribute to calculate the total value.
|
||||
* @return The finished attribute with all modifiers applied.
|
||||
*/
|
||||
public static double calculateValue(com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute attribute) {
|
||||
double base = attribute.getValue();
|
||||
for (AttributeModifier modifier : attribute.getModifiers()) {
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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.nukkitx.protocol.bedrock.packet.SetTitlePacket;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Manages the sending of a cooldown indicator to the Bedrock player as there is no cooldown indicator in Bedrock.
|
||||
* Much of the work here is from the wonderful folks from ViaRewind: https://github.com/ViaVersion/ViaRewind
|
||||
*/
|
||||
public class CooldownUtils {
|
||||
|
||||
private final static boolean SHOW_COOLDOWN;
|
||||
|
||||
static {
|
||||
SHOW_COOLDOWN = GeyserConnector.getInstance().getConfig().isShowCooldown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts sending the fake cooldown to the Bedrock client.
|
||||
* @param session GeyserSession
|
||||
*/
|
||||
public static void sendCooldown(GeyserSession session) {
|
||||
if (!SHOW_COOLDOWN) return;
|
||||
if (session.getAttackSpeed() == 0.0 || session.getAttackSpeed() > 20) return; // 0.0 usually happens on login and causes issues with visuals; anything above 20 means a plugin like OldCombatMechanics is being used
|
||||
// Needs to be sent or no subtitle packet is recognized by the client
|
||||
SetTitlePacket titlePacket = new SetTitlePacket();
|
||||
titlePacket.setType(SetTitlePacket.Type.SET_TITLE);
|
||||
titlePacket.setText(" ");
|
||||
session.sendUpstreamPacket(titlePacket);
|
||||
session.setLastHitTime(System.currentTimeMillis());
|
||||
long lastHitTime = session.getLastHitTime(); // Used later to prevent multiple scheduled cooldown threads
|
||||
computeCooldown(session, lastHitTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps updating the cooldown until the bar is complete.
|
||||
* @param session GeyserSession
|
||||
* @param lastHitTime The time of the last hit. Used to gauge how long the cooldown is taking.
|
||||
*/
|
||||
private static void computeCooldown(GeyserSession session, long lastHitTime) {
|
||||
if (session.isClosed()) return; // Don't run scheduled tasks if the client left
|
||||
if (lastHitTime != session.getLastHitTime()) return; // Means another cooldown has started so there's no need to continue this one
|
||||
SetTitlePacket titlePacket = new SetTitlePacket();
|
||||
titlePacket.setType(SetTitlePacket.Type.SET_SUBTITLE);
|
||||
titlePacket.setText(getTitle(session));
|
||||
titlePacket.setFadeInTime(0);
|
||||
titlePacket.setFadeOutTime(5);
|
||||
titlePacket.setStayTime(2);
|
||||
session.sendUpstreamPacket(titlePacket);
|
||||
if (hasCooldown(session)) {
|
||||
session.getConnector().getGeneralThreadPool().schedule(() -> computeCooldown(session, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50
|
||||
} else {
|
||||
SetTitlePacket removeTitlePacket = new SetTitlePacket();
|
||||
removeTitlePacket.setType(SetTitlePacket.Type.SET_SUBTITLE);
|
||||
removeTitlePacket.setText(" ");
|
||||
session.sendUpstreamPacket(removeTitlePacket);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasCooldown(GeyserSession session) {
|
||||
long time = System.currentTimeMillis() - session.getLastHitTime();
|
||||
double cooldown = restrain(((double) time) * session.getAttackSpeed() / 1000d, 1.5);
|
||||
return cooldown < 1.1;
|
||||
}
|
||||
|
||||
|
||||
private static double restrain(double x, double max) {
|
||||
if (x < 0d)
|
||||
return 0d;
|
||||
return Math.min(x, max);
|
||||
}
|
||||
|
||||
private static String getTitle(GeyserSession session) {
|
||||
long time = System.currentTimeMillis() - session.getLastHitTime();
|
||||
double cooldown = restrain(((double) time) * session.getAttackSpeed() / 1000d, 1);
|
||||
|
||||
int darkGrey = (int) Math.floor(10d * cooldown);
|
||||
int grey = 10 - darkGrey;
|
||||
StringBuilder builder = new StringBuilder("§8");
|
||||
while (darkGrey > 0) {
|
||||
builder.append("˙");
|
||||
darkGrey--;
|
||||
}
|
||||
builder.append("§7");
|
||||
while (grey > 0) {
|
||||
builder.append("˙");
|
||||
grey--;
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -33,6 +33,7 @@ import org.geysermc.connector.GeyserConnector;
|
|||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipFile;
|
||||
|
@ -49,7 +50,8 @@ public class LocaleUtils {
|
|||
|
||||
static {
|
||||
// Create the locales folder
|
||||
File localesFolder = new File("locales/");
|
||||
File localesFolder = GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("locales").toFile();
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
localesFolder.mkdir();
|
||||
|
||||
// Download the latest asset list and cache it
|
||||
|
@ -132,7 +134,7 @@ public class LocaleUtils {
|
|||
* @param locale Locale to download
|
||||
*/
|
||||
private static void downloadLocale(String locale) {
|
||||
File localeFile = new File("locales/" + locale + ".json");
|
||||
File localeFile = Paths.get(GeyserConnector.getInstance().getBootstrap().getConfigFolder().toString(),"locales",locale + ".json").toFile();
|
||||
|
||||
// Check if we have already downloaded the locale file
|
||||
if (localeFile.exists()) {
|
||||
|
@ -149,7 +151,7 @@ public class LocaleUtils {
|
|||
|
||||
// Get the hash and download the locale
|
||||
String hash = ASSET_MAP.get("minecraft/lang/" + locale + ".json").getHash();
|
||||
WebUtils.downloadFile("http://resources.download.minecraft.net/" + hash.substring(0, 2) + "/" + hash, "locales/" + locale + ".json");
|
||||
WebUtils.downloadFile("http://resources.download.minecraft.net/" + hash.substring(0, 2) + "/" + hash, localeFile.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,10 +207,11 @@ public class LocaleUtils {
|
|||
GeyserConnector.getInstance().getLogger().debug("Download URL: " + smallestURL);
|
||||
|
||||
// Download the smallest JAR (client or server)
|
||||
WebUtils.downloadFile(smallestURL, "tmp_locale.jar");
|
||||
Path tmpFilePath = GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("tmp_locale.jar");
|
||||
WebUtils.downloadFile(smallestURL, tmpFilePath.toString());
|
||||
|
||||
// Load in the JAR as a zip and extract the file
|
||||
ZipFile localeJar = new ZipFile("tmp_locale.jar");
|
||||
ZipFile localeJar = new ZipFile(tmpFilePath.toString());
|
||||
InputStream inputStream = localeJar.getInputStream(localeJar.getEntry("assets/minecraft/lang/en_us.json"));
|
||||
FileOutputStream outputStream = new FileOutputStream(localeFile);
|
||||
|
||||
|
@ -227,7 +230,7 @@ public class LocaleUtils {
|
|||
localeJar.close();
|
||||
|
||||
// Delete the nolonger needed client/server jar
|
||||
Files.delete(Paths.get("tmp_locale.jar"));
|
||||
Files.delete(tmpFilePath);
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError("Unable to download and extract en_us locale!", e);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
# --------------------------------
|
||||
|
||||
bedrock:
|
||||
# The IP address that will listen for connections
|
||||
# The IP address that will listen for connections.
|
||||
# There is no reason to change this unless you want to limit what IPs can connect to your server.
|
||||
address: 0.0.0.0
|
||||
# The port that will listen for connections
|
||||
port: 19132
|
||||
|
@ -74,6 +75,9 @@ allow-third-party-capes: true
|
|||
# MinecraftCapes
|
||||
allow-third-party-ears: false
|
||||
|
||||
# Allow a fake cooldown indicator to be sent. Bedrock players do not see a cooldown as they still use 1.8 combat
|
||||
show-cooldown: true
|
||||
|
||||
# The default locale if we dont have the one the client requested
|
||||
default-locale: en_us
|
||||
|
||||
|
|
Loading…
Reference in a new issue