Compare commits
3 Commits
87d98d3bb2
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
480cf742c8 | ||
|
|
66b236b50e | ||
|
|
3fbbfeb54b |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
build/
|
||||
bin/
|
||||
.kotlin
|
||||
.idea
|
||||
|
||||
@@ -15,6 +14,12 @@ out/
|
||||
!**/src/main/**/out/
|
||||
!**/src/test/**/out/
|
||||
|
||||
### MCP adds eclipser
|
||||
bin/
|
||||
.classpath
|
||||
.project
|
||||
.eclipse
|
||||
|
||||
# Gradle Wrapper
|
||||
gradlew
|
||||
gradlew.bat
|
||||
|
||||
@@ -2,7 +2,9 @@ package com.hypixel.hytale.builtin.adventure.teleporter;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.teleporter.component.Teleporter;
|
||||
import com.hypixel.hytale.builtin.adventure.teleporter.interaction.server.TeleporterInteraction;
|
||||
import com.hypixel.hytale.builtin.adventure.teleporter.interaction.server.UsedTeleporter;
|
||||
import com.hypixel.hytale.builtin.adventure.teleporter.page.TeleporterSettingsPageSupplier;
|
||||
import com.hypixel.hytale.builtin.adventure.teleporter.system.ClearUsedTeleporterSystem;
|
||||
import com.hypixel.hytale.builtin.adventure.teleporter.system.CreateWarpWhenTeleporterPlacedSystem;
|
||||
import com.hypixel.hytale.builtin.teleport.TeleportPlugin;
|
||||
import com.hypixel.hytale.component.AddReason;
|
||||
@@ -19,12 +21,14 @@ import com.hypixel.hytale.server.core.modules.interaction.interaction.config.ser
|
||||
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
|
||||
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class TeleporterPlugin extends JavaPlugin {
|
||||
private static TeleporterPlugin instance;
|
||||
private ComponentType<ChunkStore, Teleporter> teleporterComponentType;
|
||||
private ComponentType<EntityStore, UsedTeleporter> usedTeleporterComponentType;
|
||||
|
||||
public static TeleporterPlugin get() {
|
||||
return instance;
|
||||
@@ -41,6 +45,8 @@ public class TeleporterPlugin extends JavaPlugin {
|
||||
this.getChunkStoreRegistry().registerSystem(new TeleporterPlugin.TeleporterOwnedWarpRefChangeSystem());
|
||||
this.getChunkStoreRegistry().registerSystem(new TeleporterPlugin.TeleporterOwnedWarpRefSystem());
|
||||
this.getChunkStoreRegistry().registerSystem(new CreateWarpWhenTeleporterPlacedSystem());
|
||||
this.usedTeleporterComponentType = this.getEntityStoreRegistry().registerComponent(UsedTeleporter.class, UsedTeleporter::new);
|
||||
this.getEntityStoreRegistry().registerSystem(new ClearUsedTeleporterSystem());
|
||||
this.getCodecRegistry(Interaction.CODEC).register("Teleporter", TeleporterInteraction.class, TeleporterInteraction.CODEC);
|
||||
this.getCodecRegistry(OpenCustomUIInteraction.PAGE_CODEC)
|
||||
.register("Teleporter", TeleporterSettingsPageSupplier.class, TeleporterSettingsPageSupplier.CODEC);
|
||||
@@ -50,6 +56,10 @@ public class TeleporterPlugin extends JavaPlugin {
|
||||
return this.teleporterComponentType;
|
||||
}
|
||||
|
||||
public ComponentType<EntityStore, UsedTeleporter> getUsedTeleporterComponentType() {
|
||||
return this.usedTeleporterComponentType;
|
||||
}
|
||||
|
||||
private static class TeleporterOwnedWarpRefChangeSystem extends RefChangeSystem<ChunkStore, Teleporter> {
|
||||
@Nonnull
|
||||
@Override
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.hypixel.hytale.builtin.adventure.teleporter.component.Teleporter;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.component.Archetype;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
@@ -49,10 +50,30 @@ public class TeleporterInteraction extends SimpleBlockInteraction {
|
||||
)
|
||||
.documentation("The particle to play on the entity when teleporting.")
|
||||
.add()
|
||||
.<Double>appendInherited(
|
||||
new KeyedCodec<>("ClearOutXZ", Codec.DOUBLE),
|
||||
(interaction, s) -> interaction.clearoutXZ = s,
|
||||
interaction -> interaction.clearoutXZ,
|
||||
(interaction, parent) -> interaction.clearoutXZ = parent.clearoutXZ
|
||||
)
|
||||
.addValidator(Validators.greaterThanOrEqual(0.0))
|
||||
.documentation("Upon reaching the warp destination, how far away one has to move on the XZ plane in order to use another Teleporter.")
|
||||
.add()
|
||||
.<Double>appendInherited(
|
||||
new KeyedCodec<>("ClearOutY", Codec.DOUBLE),
|
||||
(interaction, s) -> interaction.clearoutY = s,
|
||||
interaction -> interaction.clearoutY,
|
||||
(interaction, parent) -> interaction.clearoutY = parent.clearoutY
|
||||
)
|
||||
.addValidator(Validators.greaterThanOrEqual(0.0))
|
||||
.documentation("Upon reaching the warp destination, how far away one has to move along the Y axis in order to use another Teleporter.")
|
||||
.add()
|
||||
.build();
|
||||
private static final Duration TELEPORT_GLOBAL_COOLDOWN = Duration.ofMillis(250L);
|
||||
private static final Duration TELEPORTER_GLOBAL_COOLDOWN = Duration.ofMillis(100L);
|
||||
@Nullable
|
||||
private String particle;
|
||||
private double clearoutXZ = 1.3;
|
||||
private double clearoutY = 2.5;
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@@ -88,38 +109,49 @@ public class TeleporterInteraction extends SimpleBlockInteraction {
|
||||
if (playerComponent == null || !playerComponent.isWaitingForClientReady()) {
|
||||
Archetype<EntityStore> archetype = commandBuffer.getArchetype(ref);
|
||||
if (!archetype.contains(Teleport.getComponentType()) && !archetype.contains(PendingTeleport.getComponentType())) {
|
||||
WorldChunk worldChunkComponent = chunkRef.getStore().getComponent(chunkRef, WorldChunk.getComponentType());
|
||||
if (!archetype.contains(UsedTeleporter.getComponentType())) {
|
||||
WorldChunk worldChunkComponent = chunkRef.getStore().getComponent(chunkRef, WorldChunk.getComponentType());
|
||||
if (worldChunkComponent != null) {
|
||||
BlockType blockType = worldChunkComponent.getBlockType(targetBlock.x, targetBlock.y, targetBlock.z);
|
||||
if (blockType != null) {
|
||||
if (!teleporter.isValid()) {
|
||||
String currentState = blockType.getStateForBlock(blockType);
|
||||
if (!"default".equals(currentState)) {
|
||||
BlockType variantBlockType = blockType.getBlockForState("default");
|
||||
if (variantBlockType != null) {
|
||||
worldChunkComponent.setBlockInteractionState(
|
||||
targetBlock.x, targetBlock.y, targetBlock.z, variantBlockType, "default", true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert worldChunkComponent != null;
|
||||
|
||||
BlockType blockType = worldChunkComponent.getBlockType(targetBlock.x, targetBlock.y, targetBlock.z);
|
||||
if (!teleporter.isValid()) {
|
||||
String currentState = blockType.getStateForBlock(blockType);
|
||||
if (!"default".equals(currentState)) {
|
||||
BlockType variantBlockType = blockType.getBlockForState("default");
|
||||
if (variantBlockType != null) {
|
||||
worldChunkComponent.setBlockInteractionState(targetBlock.x, targetBlock.y, targetBlock.z, variantBlockType, "default", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TransformComponent transformComponent = commandBuffer.getComponent(ref, TransformComponent.getComponentType());
|
||||
|
||||
assert transformComponent != null;
|
||||
|
||||
Teleport teleportComponent = teleporter.toTeleport(transformComponent.getPosition(), transformComponent.getRotation(), targetBlock);
|
||||
if (teleportComponent != null) {
|
||||
TeleportRecord recorder = commandBuffer.getComponent(ref, TeleportRecord.getComponentType());
|
||||
if (recorder == null || recorder.hasElapsedSinceLastTeleport(TELEPORT_GLOBAL_COOLDOWN)) {
|
||||
commandBuffer.addComponent(ref, Teleport.getComponentType(), teleportComponent);
|
||||
if (this.particle != null) {
|
||||
Vector3d particlePosition = transformComponent.getPosition();
|
||||
SpatialResource<Ref<EntityStore>, EntityStore> playerSpatialResource = commandBuffer.getResource(
|
||||
EntityModule.get().getPlayerSpatialResourceType()
|
||||
);
|
||||
ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
|
||||
playerSpatialResource.getSpatialStructure().collect(particlePosition, 75.0, results);
|
||||
ParticleUtil.spawnParticleEffect(this.particle, particlePosition, results, commandBuffer);
|
||||
TransformComponent transformComponent = commandBuffer.getComponent(ref, TransformComponent.getComponentType());
|
||||
if (transformComponent != null) {
|
||||
Teleport teleportComponent = teleporter.toTeleport(
|
||||
transformComponent.getPosition(), transformComponent.getRotation(), targetBlock
|
||||
);
|
||||
if (teleportComponent != null) {
|
||||
TeleportRecord recorder = commandBuffer.getComponent(ref, TeleportRecord.getComponentType());
|
||||
if (recorder == null || recorder.hasElapsedSinceLastTeleport(TELEPORTER_GLOBAL_COOLDOWN)) {
|
||||
commandBuffer.addComponent(ref, Teleport.getComponentType(), teleportComponent);
|
||||
commandBuffer.addComponent(
|
||||
ref,
|
||||
UsedTeleporter.getComponentType(),
|
||||
new UsedTeleporter(teleporter.getWorldUuid(), teleportComponent.getPosition(), this.clearoutXZ, this.clearoutY)
|
||||
);
|
||||
if (this.particle != null) {
|
||||
Vector3d particlePosition = transformComponent.getPosition();
|
||||
SpatialResource<Ref<EntityStore>, EntityStore> playerSpatialResource = commandBuffer.getResource(
|
||||
EntityModule.get().getPlayerSpatialResourceType()
|
||||
);
|
||||
ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
|
||||
playerSpatialResource.getSpatialStructure().collect(particlePosition, 75.0, results);
|
||||
ParticleUtil.spawnParticleEffect(this.particle, particlePosition, results, commandBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.hypixel.hytale.builtin.adventure.teleporter.interaction.server;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.teleporter.TeleporterPlugin;
|
||||
import com.hypixel.hytale.component.Component;
|
||||
import com.hypixel.hytale.component.ComponentType;
|
||||
import com.hypixel.hytale.math.vector.Vector3d;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
|
||||
|
||||
public class UsedTeleporter implements Component<EntityStore> {
|
||||
@Nullable
|
||||
private UUID destinationWorldUuid;
|
||||
private Vector3d destinationPosition;
|
||||
private double clearOutXZ;
|
||||
private double clearOutXZSquared;
|
||||
private double clearOutY;
|
||||
|
||||
public static ComponentType<EntityStore, UsedTeleporter> getComponentType() {
|
||||
return TeleporterPlugin.get().getUsedTeleporterComponentType();
|
||||
}
|
||||
|
||||
public UsedTeleporter() {
|
||||
}
|
||||
|
||||
public UsedTeleporter(@Nullable UUID destinationWorldUuid, Vector3d destinationPosition, double clearOutXZ, double clearOutY) {
|
||||
this.destinationWorldUuid = destinationWorldUuid;
|
||||
this.destinationPosition = destinationPosition;
|
||||
this.clearOutXZ = clearOutXZ;
|
||||
this.clearOutXZSquared = clearOutXZ * clearOutXZ;
|
||||
this.clearOutY = clearOutY;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public UUID getDestinationWorldUuid() {
|
||||
return this.destinationWorldUuid;
|
||||
}
|
||||
|
||||
public Vector3d getDestinationPosition() {
|
||||
return this.destinationPosition;
|
||||
}
|
||||
|
||||
public double getClearOutXZ() {
|
||||
return this.clearOutXZ;
|
||||
}
|
||||
|
||||
public double getClearOutXZSquared() {
|
||||
return this.clearOutXZSquared;
|
||||
}
|
||||
|
||||
public double getClearOutY() {
|
||||
return this.clearOutY;
|
||||
}
|
||||
|
||||
@NullableDecl
|
||||
@Override
|
||||
public Component<EntityStore> clone() {
|
||||
UsedTeleporter clone = new UsedTeleporter();
|
||||
clone.destinationWorldUuid = this.destinationWorldUuid;
|
||||
clone.destinationPosition = this.destinationPosition.clone();
|
||||
clone.clearOutXZ = this.clearOutXZ;
|
||||
clone.clearOutXZSquared = this.clearOutXZSquared;
|
||||
clone.clearOutY = this.clearOutY;
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.hypixel.hytale.builtin.adventure.teleporter.system;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.teleporter.interaction.server.UsedTeleporter;
|
||||
import com.hypixel.hytale.component.ArchetypeChunk;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.component.query.Query;
|
||||
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
|
||||
import com.hypixel.hytale.math.vector.Vector2d;
|
||||
import com.hypixel.hytale.math.vector.Vector3d;
|
||||
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
import org.checkerframework.checker.nullness.compatqual.NonNullDecl;
|
||||
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
|
||||
|
||||
public class ClearUsedTeleporterSystem extends EntityTickingSystem<EntityStore> {
|
||||
@Override
|
||||
public void tick(
|
||||
float dt,
|
||||
int index,
|
||||
@NonNullDecl ArchetypeChunk<EntityStore> archetypeChunk,
|
||||
@NonNullDecl Store<EntityStore> store,
|
||||
@NonNullDecl CommandBuffer<EntityStore> commandBuffer
|
||||
) {
|
||||
World world = store.getExternalData().getWorld();
|
||||
UsedTeleporter usedTeleporter = archetypeChunk.getComponent(index, UsedTeleporter.getComponentType());
|
||||
TransformComponent transformComponent = archetypeChunk.getComponent(index, TransformComponent.getComponentType());
|
||||
if (shouldClear(world, usedTeleporter, transformComponent)) {
|
||||
Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
|
||||
commandBuffer.removeComponent(ref, UsedTeleporter.getComponentType());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldClear(World entityWorld, UsedTeleporter usedTeleporter, @Nullable TransformComponent transformComponent) {
|
||||
if (transformComponent == null) {
|
||||
return true;
|
||||
} else {
|
||||
UUID destinationWorldUuid = usedTeleporter.getDestinationWorldUuid();
|
||||
if (destinationWorldUuid != null && !entityWorld.getWorldConfig().getUuid().equals(destinationWorldUuid)) {
|
||||
return true;
|
||||
} else {
|
||||
Vector3d entityPosition = transformComponent.getPosition();
|
||||
Vector3d destinationPosition = usedTeleporter.getDestinationPosition();
|
||||
double deltaY = Math.abs(entityPosition.y - destinationPosition.y);
|
||||
double distanceXZsq = Vector2d.distanceSquared(entityPosition.x, entityPosition.z, destinationPosition.x, destinationPosition.z);
|
||||
return deltaY > usedTeleporter.getClearOutY() || distanceXZsq > usedTeleporter.getClearOutXZ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NullableDecl
|
||||
@Override
|
||||
public Query<EntityStore> getQuery() {
|
||||
return UsedTeleporter.getComponentType();
|
||||
}
|
||||
}
|
||||
@@ -65,22 +65,28 @@ public class AssetEditorGamePacketHandler implements SubPacketHandler {
|
||||
if (ref != null && ref.isValid()) {
|
||||
Store<EntityStore> store = ref.getStore();
|
||||
World world = store.getExternalData().getWorld();
|
||||
CompletableFuture.runAsync(() -> {
|
||||
world.execute(
|
||||
() -> {
|
||||
Player playerComponent = store.getComponent(ref, Player.getComponentType());
|
||||
if (!this.lacksPermission(playerComponent, true)) {
|
||||
;
|
||||
CompletableFuture.runAsync(
|
||||
() -> {
|
||||
LOGGER.at(Level.INFO).log("%s updating json asset at %s", this.packetHandler.getPlayerRef().getUsername(), packet.path);
|
||||
EditorClient mockClient = new EditorClient(playerRef);
|
||||
AssetEditorPlugin.get()
|
||||
.handleJsonAssetUpdate(
|
||||
mockClient,
|
||||
packet.path != null ? new AssetPath(packet.path) : null,
|
||||
packet.assetType,
|
||||
packet.assetIndex,
|
||||
packet.commands,
|
||||
packet.token
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}, world)
|
||||
.thenRunAsync(
|
||||
() -> {
|
||||
LOGGER.at(Level.INFO).log("%s updating json asset at %s", this.packetHandler.getPlayerRef().getUsername(), packet.path);
|
||||
EditorClient mockClient = new EditorClient(playerRef);
|
||||
AssetEditorPlugin.get()
|
||||
.handleJsonAssetUpdate(
|
||||
mockClient, packet.path != null ? new AssetPath(packet.path) : null, packet.assetType, packet.assetIndex, packet.commands, packet.token
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
throw new RuntimeException("Unable to process AssetEditorUpdateJsonAsset packet. Player ref is invalid!");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.hypixel.hytale.server.core.auth;
|
||||
|
||||
import com.hypixel.hytale.common.util.java.ManifestUtil;
|
||||
import java.time.Duration;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class AuthConfig {
|
||||
@@ -17,7 +18,7 @@ public class AuthConfig {
|
||||
public static final String SCOPE_CLIENT = "hytale:client";
|
||||
public static final String SCOPE_SERVER = "hytale:server";
|
||||
public static final String SCOPE_EDITOR = "hytale:editor";
|
||||
public static final int HTTP_TIMEOUT_SECONDS = 10;
|
||||
public static final Duration HTTP_TIMEOUT = Duration.ofSeconds(30L);
|
||||
public static final int DEVICE_POLL_INTERVAL_SECONDS = 15;
|
||||
public static final String ENV_SERVER_AUDIENCE = "HYTALE_SERVER_AUDIENCE";
|
||||
public static final String ENV_SERVER_IDENTITY_TOKEN = "HYTALE_SERVER_IDENTITY_TOKEN";
|
||||
|
||||
@@ -10,13 +10,13 @@ import com.nimbusds.jwt.JWTClaimsSet;
|
||||
import com.nimbusds.jwt.SignedJWT;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.text.ParseException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -28,14 +28,14 @@ public class JWTValidator {
|
||||
private static final JWSAlgorithm SUPPORTED_ALGORITHM = JWSAlgorithm.EdDSA;
|
||||
private static final int MIN_SIGNATURE_LENGTH = 80;
|
||||
private static final int MAX_SIGNATURE_LENGTH = 90;
|
||||
private static final Duration JWKS_REFRESH_MIN_INTERVAL = Duration.ofMinutes(5L);
|
||||
private final SessionServiceClient sessionServiceClient;
|
||||
private final String expectedIssuer;
|
||||
private final String expectedAudience;
|
||||
private volatile JWKSet cachedJwkSet;
|
||||
private volatile long jwksCacheExpiry;
|
||||
private final long jwksCacheDurationMs = TimeUnit.HOURS.toMillis(1L);
|
||||
private final ReentrantLock jwksFetchLock = new ReentrantLock();
|
||||
private volatile CompletableFuture<JWKSet> pendingFetch = null;
|
||||
private volatile Instant lastJwksRefresh;
|
||||
|
||||
public JWTValidator(@Nonnull SessionServiceClient sessionServiceClient, @Nonnull String expectedIssuer, @Nonnull String expectedAudience) {
|
||||
this.sessionServiceClient = sessionServiceClient;
|
||||
@@ -171,15 +171,14 @@ public class JWTValidator {
|
||||
|
||||
@Nullable
|
||||
private JWKSet getJwkSet(boolean forceRefresh) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (!forceRefresh && this.cachedJwkSet != null && now < this.jwksCacheExpiry) {
|
||||
if (!forceRefresh && this.cachedJwkSet != null) {
|
||||
return this.cachedJwkSet;
|
||||
} else {
|
||||
this.jwksFetchLock.lock();
|
||||
|
||||
JWKSet var5;
|
||||
JWKSet var3;
|
||||
try {
|
||||
if (!forceRefresh && this.cachedJwkSet != null && now < this.jwksCacheExpiry) {
|
||||
if (!forceRefresh && this.cachedJwkSet != null) {
|
||||
return this.cachedJwkSet;
|
||||
}
|
||||
|
||||
@@ -196,7 +195,7 @@ public class JWTValidator {
|
||||
this.jwksFetchLock.unlock();
|
||||
|
||||
try {
|
||||
var5 = existing.join();
|
||||
var3 = existing.join();
|
||||
} finally {
|
||||
this.jwksFetchLock.lock();
|
||||
}
|
||||
@@ -204,7 +203,7 @@ public class JWTValidator {
|
||||
this.jwksFetchLock.unlock();
|
||||
}
|
||||
|
||||
return var5;
|
||||
return var3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,8 +227,8 @@ public class JWTValidator {
|
||||
} else {
|
||||
JWKSet newSet = new JWKSet(jwkList);
|
||||
this.cachedJwkSet = newSet;
|
||||
this.jwksCacheExpiry = System.currentTimeMillis() + this.jwksCacheDurationMs;
|
||||
LOGGER.at(Level.INFO).log("JWKS loaded with %d keys", jwkList.size());
|
||||
this.lastJwksRefresh = Instant.now();
|
||||
LOGGER.at(Level.INFO).log("JWKS loaded with %d keys (cached permanently)", jwkList.size());
|
||||
return newSet;
|
||||
}
|
||||
} catch (Exception var8) {
|
||||
@@ -248,6 +247,9 @@ public class JWTValidator {
|
||||
return false;
|
||||
} else if (this.verifySignature(signedJWT, jwkSet)) {
|
||||
return true;
|
||||
} else if (!this.canForceRefreshJwks()) {
|
||||
LOGGER.at(Level.FINE).log("Signature verification failed but JWKS was refreshed recently; skipping refresh");
|
||||
return false;
|
||||
} else {
|
||||
LOGGER.at(Level.INFO).log("Signature verification failed with cached JWKS, retrying with fresh keys");
|
||||
JWKSet freshJwkSet = this.getJwkSet(true);
|
||||
@@ -255,6 +257,11 @@ public class JWTValidator {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canForceRefreshJwks() {
|
||||
Instant lastRefresh = this.lastJwksRefresh;
|
||||
return lastRefresh == null ? true : Duration.between(lastRefresh, Instant.now()).compareTo(JWKS_REFRESH_MIN_INTERVAL) >= 0;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JWK convertToJWK(SessionServiceClient.JwkKey key) {
|
||||
if (!"OKP".equals(key.kty)) {
|
||||
@@ -276,7 +283,6 @@ public class JWTValidator {
|
||||
|
||||
try {
|
||||
this.cachedJwkSet = null;
|
||||
this.jwksCacheExpiry = 0L;
|
||||
this.pendingFetch = null;
|
||||
} finally {
|
||||
this.jwksFetchLock.unlock();
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.util.RawJsonReader;
|
||||
import com.hypixel.hytale.logger.HytaleLogger;
|
||||
import com.hypixel.hytale.server.core.util.ServiceHttpClientFactory;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
@@ -14,7 +15,6 @@ import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.net.http.HttpResponse.BodyHandlers;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
@@ -23,14 +23,13 @@ import javax.annotation.Nullable;
|
||||
|
||||
public class ProfileServiceClient {
|
||||
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
|
||||
private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(5L);
|
||||
private final HttpClient httpClient;
|
||||
private final String profileServiceUrl;
|
||||
|
||||
public ProfileServiceClient(@Nonnull String profileServiceUrl) {
|
||||
if (profileServiceUrl != null && !profileServiceUrl.isEmpty()) {
|
||||
this.profileServiceUrl = profileServiceUrl.endsWith("/") ? profileServiceUrl.substring(0, profileServiceUrl.length() - 1) : profileServiceUrl;
|
||||
this.httpClient = HttpClient.newBuilder().connectTimeout(REQUEST_TIMEOUT).build();
|
||||
this.httpClient = ServiceHttpClientFactory.create(AuthConfig.HTTP_TIMEOUT);
|
||||
LOGGER.at(Level.INFO).log("Profile Service client initialized for: %s", this.profileServiceUrl);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Profile Service URL cannot be null or empty");
|
||||
@@ -45,7 +44,7 @@ public class ProfileServiceClient {
|
||||
.header("Accept", "application/json")
|
||||
.header("Authorization", "Bearer " + bearerToken)
|
||||
.header("User-Agent", AuthConfig.USER_AGENT)
|
||||
.timeout(REQUEST_TIMEOUT)
|
||||
.timeout(AuthConfig.HTTP_TIMEOUT)
|
||||
.GET()
|
||||
.build();
|
||||
LOGGER.at(Level.FINE).log("Fetching profile by UUID: %s", uuid);
|
||||
@@ -90,7 +89,7 @@ public class ProfileServiceClient {
|
||||
.header("Accept", "application/json")
|
||||
.header("Authorization", "Bearer " + bearerToken)
|
||||
.header("User-Agent", AuthConfig.USER_AGENT)
|
||||
.timeout(REQUEST_TIMEOUT)
|
||||
.timeout(AuthConfig.HTTP_TIMEOUT)
|
||||
.GET()
|
||||
.build();
|
||||
LOGGER.at(Level.FINE).log("Fetching profile by username: %s", username);
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
|
||||
import com.hypixel.hytale.codec.util.RawJsonReader;
|
||||
import com.hypixel.hytale.logger.HytaleLogger;
|
||||
import com.hypixel.hytale.server.core.util.ServiceHttpClientFactory;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
@@ -14,24 +15,25 @@ import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.net.http.HttpRequest.BodyPublishers;
|
||||
import java.net.http.HttpResponse.BodyHandlers;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class SessionServiceClient {
|
||||
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
|
||||
private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(5L);
|
||||
private static final ExecutorService HTTP_EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
|
||||
private final HttpClient httpClient;
|
||||
private final String sessionServiceUrl;
|
||||
|
||||
public SessionServiceClient(@Nonnull String sessionServiceUrl) {
|
||||
if (sessionServiceUrl != null && !sessionServiceUrl.isEmpty()) {
|
||||
this.sessionServiceUrl = sessionServiceUrl.endsWith("/") ? sessionServiceUrl.substring(0, sessionServiceUrl.length() - 1) : sessionServiceUrl;
|
||||
this.httpClient = HttpClient.newBuilder().connectTimeout(REQUEST_TIMEOUT).build();
|
||||
this.httpClient = ServiceHttpClientFactory.create(AuthConfig.HTTP_TIMEOUT);
|
||||
LOGGER.at(Level.INFO).log("Session Service client initialized for: %s", this.sessionServiceUrl);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Session Service URL cannot be null or empty");
|
||||
@@ -49,7 +51,7 @@ public class SessionServiceClient {
|
||||
.header("Accept", "application/json")
|
||||
.header("Authorization", "Bearer " + bearerToken)
|
||||
.header("User-Agent", AuthConfig.USER_AGENT)
|
||||
.timeout(REQUEST_TIMEOUT)
|
||||
.timeout(AuthConfig.HTTP_TIMEOUT)
|
||||
.POST(BodyPublishers.ofString(jsonBody))
|
||||
.build();
|
||||
LOGGER.at(Level.INFO).log("Requesting authorization grant with identity token, aud='%s'", serverAudience);
|
||||
@@ -79,7 +81,8 @@ public class SessionServiceClient {
|
||||
LOGGER.at(Level.WARNING).log("Unexpected error requesting authorization grant: %s", var10.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
HTTP_EXECUTOR
|
||||
);
|
||||
}
|
||||
|
||||
@@ -98,7 +101,7 @@ public class SessionServiceClient {
|
||||
.header("Accept", "application/json")
|
||||
.header("Authorization", "Bearer " + bearerToken)
|
||||
.header("User-Agent", AuthConfig.USER_AGENT)
|
||||
.timeout(REQUEST_TIMEOUT)
|
||||
.timeout(AuthConfig.HTTP_TIMEOUT)
|
||||
.POST(BodyPublishers.ofString(jsonBody))
|
||||
.build();
|
||||
LOGGER.at(Level.INFO).log("Exchanging authorization grant for access token");
|
||||
@@ -130,7 +133,8 @@ public class SessionServiceClient {
|
||||
LOGGER.at(Level.WARNING).log("Unexpected error exchanging auth grant: %s", var10.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
HTTP_EXECUTOR
|
||||
);
|
||||
}
|
||||
|
||||
@@ -141,7 +145,7 @@ public class SessionServiceClient {
|
||||
.uri(URI.create(this.sessionServiceUrl + "/.well-known/jwks.json"))
|
||||
.header("Accept", "application/json")
|
||||
.header("User-Agent", AuthConfig.USER_AGENT)
|
||||
.timeout(REQUEST_TIMEOUT)
|
||||
.timeout(AuthConfig.HTTP_TIMEOUT)
|
||||
.GET()
|
||||
.build();
|
||||
LOGGER.at(Level.FINE).log("Fetching JWKS from Session Service");
|
||||
@@ -181,7 +185,7 @@ public class SessionServiceClient {
|
||||
.header("Accept", "application/json")
|
||||
.header("Authorization", "Bearer " + oauthAccessToken)
|
||||
.header("User-Agent", AuthConfig.USER_AGENT)
|
||||
.timeout(REQUEST_TIMEOUT)
|
||||
.timeout(AuthConfig.HTTP_TIMEOUT)
|
||||
.GET()
|
||||
.build();
|
||||
LOGGER.at(Level.INFO).log("Fetching game profiles...");
|
||||
@@ -221,7 +225,7 @@ public class SessionServiceClient {
|
||||
.header("Content-Type", "application/json")
|
||||
.header("Authorization", "Bearer " + oauthAccessToken)
|
||||
.header("User-Agent", AuthConfig.USER_AGENT)
|
||||
.timeout(REQUEST_TIMEOUT)
|
||||
.timeout(AuthConfig.HTTP_TIMEOUT)
|
||||
.POST(BodyPublishers.ofString(body))
|
||||
.build();
|
||||
LOGGER.at(Level.INFO).log("Creating game session...");
|
||||
@@ -262,7 +266,7 @@ public class SessionServiceClient {
|
||||
.header("Accept", "application/json")
|
||||
.header("Authorization", "Bearer " + sessionToken)
|
||||
.header("User-Agent", AuthConfig.USER_AGENT)
|
||||
.timeout(REQUEST_TIMEOUT)
|
||||
.timeout(AuthConfig.HTTP_TIMEOUT)
|
||||
.POST(BodyPublishers.noBody())
|
||||
.build();
|
||||
LOGGER.at(Level.INFO).log("Refreshing game session...");
|
||||
@@ -292,7 +296,8 @@ public class SessionServiceClient {
|
||||
LOGGER.at(Level.WARNING).log("Unexpected error refreshing session: %s", var7.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
HTTP_EXECUTOR
|
||||
);
|
||||
}
|
||||
|
||||
@@ -303,7 +308,7 @@ public class SessionServiceClient {
|
||||
.uri(URI.create(this.sessionServiceUrl + "/game-session"))
|
||||
.header("Authorization", "Bearer " + sessionToken)
|
||||
.header("User-Agent", AuthConfig.USER_AGENT)
|
||||
.timeout(REQUEST_TIMEOUT)
|
||||
.timeout(AuthConfig.HTTP_TIMEOUT)
|
||||
.DELETE()
|
||||
.build();
|
||||
LOGGER.at(Level.INFO).log("Terminating game session...");
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.google.gson.JsonParser;
|
||||
import com.hypixel.hytale.logger.HytaleLogger;
|
||||
import com.hypixel.hytale.server.core.HytaleServer;
|
||||
import com.hypixel.hytale.server.core.auth.AuthConfig;
|
||||
import com.hypixel.hytale.server.core.util.ServiceHttpClientFactory;
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -20,7 +21,6 @@ import java.net.http.HttpResponse.BodyHandlers;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -34,7 +34,7 @@ import javax.annotation.Nullable;
|
||||
public class OAuthClient {
|
||||
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
|
||||
private static final SecureRandom RANDOM = new SecureRandom();
|
||||
private final HttpClient httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10L)).build();
|
||||
private final HttpClient httpClient = ServiceHttpClientFactory.create(AuthConfig.HTTP_TIMEOUT);
|
||||
|
||||
public Runnable startFlow(@Nonnull OAuthBrowserFlow flow) {
|
||||
AtomicBoolean cancelled = new AtomicBoolean(false);
|
||||
|
||||
@@ -10,7 +10,6 @@ import com.hypixel.hytale.function.function.TriFunction;
|
||||
import com.hypixel.hytale.logger.HytaleLogger;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector4d;
|
||||
import com.hypixel.hytale.metrics.metric.HistoricMetric;
|
||||
import com.hypixel.hytale.protocol.BlockPosition;
|
||||
import com.hypixel.hytale.protocol.ForkedChainId;
|
||||
import com.hypixel.hytale.protocol.GameMode;
|
||||
@@ -44,11 +43,6 @@ import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import com.hypixel.hytale.server.core.util.UUIDUtil;
|
||||
import io.sentry.Sentry;
|
||||
import io.sentry.SentryEvent;
|
||||
import io.sentry.SentryLevel;
|
||||
import io.sentry.protocol.Message;
|
||||
import io.sentry.protocol.SentryId;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
@@ -56,7 +50,6 @@ import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -197,7 +190,7 @@ public class InteractionManager implements Component<EntityStore> {
|
||||
int highestChainId = -1;
|
||||
boolean changed = false;
|
||||
|
||||
label99:
|
||||
label114:
|
||||
while (it.hasNext()) {
|
||||
SyncInteractionChain packet = it.next();
|
||||
if (packet.desync) {
|
||||
@@ -233,7 +226,7 @@ public class InteractionManager implements Component<EntityStore> {
|
||||
it.remove();
|
||||
this.packetQueueTime = 0L;
|
||||
}
|
||||
continue label99;
|
||||
continue label114;
|
||||
}
|
||||
|
||||
chain = subChain;
|
||||
@@ -241,50 +234,51 @@ public class InteractionManager implements Component<EntityStore> {
|
||||
}
|
||||
|
||||
highestChainId = Math.max(highestChainId, packet.chainId);
|
||||
if (chain == null && !finished) {
|
||||
if (this.syncStart(ref, packet)) {
|
||||
boolean isProxy = packet.data != null && !UUIDUtil.isEmptyOrNull(packet.data.proxyId);
|
||||
if ((chain != null || finished) && !isProxy) {
|
||||
if (chain != null) {
|
||||
this.sync(ref, chain, packet);
|
||||
changed = true;
|
||||
it.remove();
|
||||
this.packetQueueTime = 0L;
|
||||
} else {
|
||||
if (!this.waitingForClient(ref)) {
|
||||
long queuedTime;
|
||||
if (this.packetQueueTime == 0L) {
|
||||
this.packetQueueTime = this.currentTime;
|
||||
queuedTime = 0L;
|
||||
} else {
|
||||
queuedTime = this.currentTime - this.packetQueueTime;
|
||||
}
|
||||
|
||||
HytaleLogger.Api context = LOGGER.at(Level.FINE);
|
||||
if (context.isEnabled()) {
|
||||
context.log("Queued chain %d for %s", packet.chainId, FormatUtil.nanosToString(queuedTime));
|
||||
}
|
||||
|
||||
if (queuedTime > TimeUnit.MILLISECONDS.toNanos(this.getOperationTimeoutThreshold())) {
|
||||
this.sendCancelPacket(packet.chainId, packet.forkedId);
|
||||
it.remove();
|
||||
context = LOGGER.at(Level.FINE);
|
||||
if (context.isEnabled()) {
|
||||
context.log("Discarding packet due to queuing for too long: %s", packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!desynced) {
|
||||
finished = true;
|
||||
}
|
||||
} else if (desynced) {
|
||||
this.sendCancelPacket(packet.chainId, packet.forkedId);
|
||||
it.remove();
|
||||
HytaleLogger.Api ctx = LOGGER.at(Level.FINE);
|
||||
ctx.log("Discarding packet due to desync: %s", packet);
|
||||
}
|
||||
} else if (chain != null) {
|
||||
this.sync(ref, chain, packet);
|
||||
} else if (this.syncStart(ref, packet)) {
|
||||
changed = true;
|
||||
it.remove();
|
||||
this.packetQueueTime = 0L;
|
||||
} else if (desynced) {
|
||||
this.sendCancelPacket(packet.chainId, packet.forkedId);
|
||||
it.remove();
|
||||
HytaleLogger.Api ctx = LOGGER.at(Level.FINE);
|
||||
ctx.log("Discarding packet due to desync: %s", packet);
|
||||
} else {
|
||||
if (!this.waitingForClient(ref)) {
|
||||
long queuedTime;
|
||||
if (this.packetQueueTime == 0L) {
|
||||
this.packetQueueTime = this.currentTime;
|
||||
queuedTime = 0L;
|
||||
} else {
|
||||
queuedTime = this.currentTime - this.packetQueueTime;
|
||||
}
|
||||
|
||||
HytaleLogger.Api context = LOGGER.at(Level.FINE);
|
||||
if (context.isEnabled()) {
|
||||
context.log("Queued chain %d for %s", packet.chainId, FormatUtil.nanosToString(queuedTime));
|
||||
}
|
||||
|
||||
if (queuedTime > TimeUnit.MILLISECONDS.toNanos(this.getOperationTimeoutThreshold())) {
|
||||
this.sendCancelPacket(packet.chainId, packet.forkedId);
|
||||
it.remove();
|
||||
context = LOGGER.at(Level.FINE);
|
||||
if (context.isEnabled()) {
|
||||
context.log("Discarding packet due to queuing for too long: %s", packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!desynced && !isProxy) {
|
||||
finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +357,7 @@ public class InteractionManager implements Component<EntityStore> {
|
||||
long threshold = this.getOperationTimeoutThreshold();
|
||||
TimeResource timeResource = this.commandBuffer.getResource(TimeResource.getResourceType());
|
||||
if (timeResource.getTimeDilationModifier() == 1.0F && waitMillis > threshold) {
|
||||
this.sendCancelPacket(chain);
|
||||
this.cancelChains(chain);
|
||||
return chain.getForkedChains().isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -641,41 +635,10 @@ public class InteractionManager implements Component<EntityStore> {
|
||||
|
||||
long threshold = this.getOperationTimeoutThreshold();
|
||||
if (tickTimeDilation == 1.0F && waitMillis > threshold) {
|
||||
SentryEvent event = new SentryEvent();
|
||||
event.setLevel(SentryLevel.ERROR);
|
||||
Message message = new Message();
|
||||
message.setMessage("Client failed to send client data, ending early to prevent desync");
|
||||
HashMap<String, Object> unknown = new HashMap<>();
|
||||
unknown.put("Threshold", threshold);
|
||||
unknown.put("Wait Millis", waitMillis);
|
||||
unknown.put("Current Root", chain.getRootInteraction() != null ? chain.getRootInteraction().getId() : "<null>");
|
||||
Operation innerOp = operation.getInnerOperation();
|
||||
unknown.put("Current Op", innerOp.getClass().getName());
|
||||
if (innerOp instanceof Interaction interaction) {
|
||||
unknown.put("Current Interaction", interaction.getId());
|
||||
}
|
||||
|
||||
unknown.put("Current Index", chain.getOperationIndex());
|
||||
unknown.put("Current Op Counter", chain.getOperationCounter());
|
||||
HistoricMetric metric = ref.getStore().getExternalData().getWorld().getBufferedTickLengthMetricSet();
|
||||
long[] periods = metric.getPeriodsNanos();
|
||||
|
||||
for (int i = 0; i < periods.length; i++) {
|
||||
String length = FormatUtil.timeUnitToString(periods[i], TimeUnit.NANOSECONDS, true);
|
||||
double average = metric.getAverage(i);
|
||||
long min = metric.calculateMin(i);
|
||||
long max = metric.calculateMax(i);
|
||||
String value = FormatUtil.simpleTimeUnitFormat(min, average, max, TimeUnit.NANOSECONDS, TimeUnit.MILLISECONDS, 3);
|
||||
unknown.put(String.format("World Perf %s", length), value);
|
||||
}
|
||||
|
||||
event.setExtras(unknown);
|
||||
event.setMessage(message);
|
||||
SentryId eventId = Sentry.captureEvent(event);
|
||||
LOGGER.atWarning().log("Client failed to send client data, ending early to prevent desync. %s", eventId);
|
||||
LOGGER.atWarning().log("Client failed to send client data, ending early to prevent desync.");
|
||||
chain.setServerState(InteractionState.Failed);
|
||||
chain.setClientState(InteractionState.Failed);
|
||||
this.sendCancelPacket(chain);
|
||||
this.cancelChains(chain);
|
||||
return null;
|
||||
} else {
|
||||
if (entry.consumeSendInitial() || wasWrong) {
|
||||
@@ -768,6 +731,8 @@ public class InteractionManager implements Component<EntityStore> {
|
||||
if (ctx.isEnabled()) {
|
||||
ctx.log("Got syncStart for %d-%s but packet wasn't the first.", index, packet.forkedId);
|
||||
}
|
||||
|
||||
this.sendCancelPacket(index, packet.forkedId);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -777,6 +742,7 @@ public class InteractionManager implements Component<EntityStore> {
|
||||
ctx.log("Can't start a forked chain from the client: %d %s", index, packet.forkedId);
|
||||
}
|
||||
|
||||
this.sendCancelPacket(index, packet.forkedId);
|
||||
return true;
|
||||
} else {
|
||||
InteractionType type = packet.interactionType;
|
||||
@@ -1362,7 +1328,7 @@ public class InteractionManager implements Component<EntityStore> {
|
||||
this.sendCancelPacket(chain.getChainId(), chain.getForkedChainId());
|
||||
}
|
||||
|
||||
public void sendCancelPacket(int chainId, @Nonnull ForkedChainId forkedChainId) {
|
||||
public void sendCancelPacket(int chainId, ForkedChainId forkedChainId) {
|
||||
if (this.playerRef != null) {
|
||||
this.playerRef.getPacketHandler().writeNoCache(new CancelInteractionChain(chainId, forkedChainId));
|
||||
}
|
||||
|
||||
@@ -456,13 +456,17 @@ public class Inventory implements NetworkSerializable<UpdatePlayerInventory> {
|
||||
|
||||
private boolean tryEquipArmorPart(int fromSectionId, short fromSlotId, int quantity, ItemContainer targetContainer, boolean forceEquip) {
|
||||
ItemStack itemStack = targetContainer.getItemStack(fromSlotId);
|
||||
Item item = itemStack.getItem();
|
||||
ItemArmor itemArmor = item.getArmor();
|
||||
if (itemArmor == null || fromSectionId == -3 || !forceEquip && this.armor.getItemStack((short)itemArmor.getArmorSlot().ordinal()) != null) {
|
||||
if (ItemStack.isEmpty(itemStack)) {
|
||||
return false;
|
||||
} else {
|
||||
targetContainer.moveItemStackFromSlotToSlot(fromSlotId, quantity, this.armor, (short)itemArmor.getArmorSlot().ordinal());
|
||||
return true;
|
||||
Item item = itemStack.getItem();
|
||||
ItemArmor itemArmor = item.getArmor();
|
||||
if (itemArmor == null || fromSectionId == -3 || !forceEquip && this.armor.getItemStack((short)itemArmor.getArmorSlot().ordinal()) != null) {
|
||||
return false;
|
||||
} else {
|
||||
targetContainer.moveItemStackFromSlotToSlot(fromSlotId, quantity, this.armor, (short)itemArmor.getArmorSlot().ordinal());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -410,10 +410,12 @@ public abstract class PacketHandler implements IPacketReceiver {
|
||||
Attribute<Long> loginStartAttribute = channel.attr(LOGIN_START_ATTRIBUTE_KEY);
|
||||
long now = System.nanoTime();
|
||||
Long before = loginStartAttribute.getAndSet(now);
|
||||
NettyUtil.TimeoutContext context = channel.attr(NettyUtil.TimeoutContext.KEY).get();
|
||||
String identifier = context != null ? context.playerIdentifier() : NettyUtil.formatRemoteAddress(channel);
|
||||
if (before == null) {
|
||||
LOGIN_TIMING_LOGGER.at(level).log(message);
|
||||
LOGIN_TIMING_LOGGER.at(level).log("[%s] %s", identifier, message);
|
||||
} else {
|
||||
LOGIN_TIMING_LOGGER.at(level).log("%s took %s", message, LazyArgs.lazy(() -> FormatUtil.nanosToString(now - before)));
|
||||
LOGIN_TIMING_LOGGER.at(level).log("[%s] %s took %s", identifier, message, LazyArgs.lazy(() -> FormatUtil.nanosToString(now - before)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -688,42 +688,60 @@ public class Universe extends JavaPlugin implements IMessageReceiver, MetricProv
|
||||
.getEventBus()
|
||||
.<Void, PlayerConnectEvent>dispatchFor(PlayerConnectEvent.class)
|
||||
.dispatch(new PlayerConnectEvent((Holder<EntityStore>)holder, playerRefComponent, lastWorld != null ? lastWorld : this.getDefaultWorld()));
|
||||
World world = event.getWorld() != null ? event.getWorld() : this.getDefaultWorld();
|
||||
if (world == null) {
|
||||
if (!channel.isActive()) {
|
||||
this.players.remove(uuid, playerRefComponent);
|
||||
playerConnection.disconnect("No world available to join");
|
||||
this.getLogger().at(Level.SEVERE).log("Player '%s' (%s) could not join - no default world configured", username, uuid);
|
||||
this.getLogger().at(Level.INFO).log("Player '%s' (%s) disconnected during PlayerConnectEvent, cleaned up", username, uuid);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
if (lastWorldName != null && lastWorld == null) {
|
||||
playerComponent.sendMessage(
|
||||
Message.translation("server.universe.failedToFindWorld").param("lastWorldName", lastWorldName).param("name", world.getName())
|
||||
);
|
||||
}
|
||||
|
||||
PacketHandler.logConnectionTimings(channel, "Processed Referral", Level.FINEST);
|
||||
playerRefComponent.getPacketHandler().write(new ServerTags(AssetRegistry.getClientTags()));
|
||||
return world.addPlayer(playerRefComponent, null, false, false).thenApply(p -> {
|
||||
PacketHandler.logConnectionTimings(channel, "Add to World", Level.FINEST);
|
||||
if (!channel.isActive()) {
|
||||
if (p != null) {
|
||||
playerComponent.remove();
|
||||
}
|
||||
|
||||
this.players.remove(uuid, playerRefComponent);
|
||||
this.getLogger().at(Level.WARNING).log("Player '%s' (%s) disconnected during world join, cleaned up from universe", username, uuid);
|
||||
return null;
|
||||
} else if (playerComponent.wasRemoved()) {
|
||||
this.players.remove(uuid, playerRefComponent);
|
||||
return null;
|
||||
} else {
|
||||
return (PlayerRef)p;
|
||||
}
|
||||
}).exceptionally(throwable -> {
|
||||
World world = event.getWorld() != null ? event.getWorld() : this.getDefaultWorld();
|
||||
if (world == null) {
|
||||
this.players.remove(uuid, playerRefComponent);
|
||||
playerComponent.remove();
|
||||
throw new RuntimeException("Exception when adding player to universe:", throwable);
|
||||
});
|
||||
playerConnection.disconnect("No world available to join");
|
||||
this.getLogger().at(Level.SEVERE).log("Player '%s' (%s) could not join - no default world configured", username, uuid);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
if (lastWorldName != null && lastWorld == null) {
|
||||
playerComponent.sendMessage(
|
||||
Message.translation("server.universe.failedToFindWorld").param("lastWorldName", lastWorldName).param("name", world.getName())
|
||||
);
|
||||
}
|
||||
|
||||
PacketHandler.logConnectionTimings(channel, "Processed Referral", Level.FINEST);
|
||||
playerRefComponent.getPacketHandler().write(new ServerTags(AssetRegistry.getClientTags()));
|
||||
CompletableFuture<PlayerRef> addPlayerFuture = world.addPlayer(playerRefComponent, null, false, false);
|
||||
if (addPlayerFuture == null) {
|
||||
this.players.remove(uuid, playerRefComponent);
|
||||
this.getLogger().at(Level.INFO).log("Player '%s' (%s) disconnected before world addition, cleaned up", username, uuid);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} else {
|
||||
return addPlayerFuture.<PlayerRef>thenApply(
|
||||
p -> {
|
||||
PacketHandler.logConnectionTimings(channel, "Add to World", Level.FINEST);
|
||||
if (!channel.isActive()) {
|
||||
if (p != null) {
|
||||
playerComponent.remove();
|
||||
}
|
||||
|
||||
this.players.remove(uuid, playerRefComponent);
|
||||
this.getLogger()
|
||||
.at(Level.WARNING)
|
||||
.log("Player '%s' (%s) disconnected during world join, cleaned up from universe", username, uuid);
|
||||
return null;
|
||||
} else if (playerComponent.wasRemoved()) {
|
||||
this.players.remove(uuid, playerRefComponent);
|
||||
return null;
|
||||
} else {
|
||||
return (PlayerRef)p;
|
||||
}
|
||||
}
|
||||
)
|
||||
.exceptionally(throwable -> {
|
||||
this.players.remove(uuid, playerRefComponent);
|
||||
playerComponent.remove();
|
||||
throw new RuntimeException("Exception when adding player to universe:", throwable);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class IndexedStorageChunkStorageProvider implements IChunkStorageProvider
|
||||
)
|
||||
.add()
|
||||
.build();
|
||||
private boolean flushOnWrite = true;
|
||||
private boolean flushOnWrite = false;
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.hypixel.hytale.server.core.HytaleServer;
|
||||
import com.hypixel.hytale.server.core.HytaleServerConfig;
|
||||
import com.hypixel.hytale.server.core.auth.AuthConfig;
|
||||
import com.hypixel.hytale.server.core.auth.ServerAuthManager;
|
||||
import com.hypixel.hytale.server.core.util.ServiceHttpClientFactory;
|
||||
import com.hypixel.hytale.server.core.util.io.FileUtil;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -46,7 +47,7 @@ public class UpdateService {
|
||||
private final String accountDataUrl = "https://account-data.hytale.com";
|
||||
|
||||
public UpdateService() {
|
||||
this.httpClient = HttpClient.newBuilder().connectTimeout(REQUEST_TIMEOUT).followRedirects(Redirect.NORMAL).build();
|
||||
this.httpClient = ServiceHttpClientFactory.newBuilder(REQUEST_TIMEOUT).followRedirects(Redirect.NORMAL).build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.hypixel.hytale.server.core.util;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpClient.Builder;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public final class ServiceHttpClientFactory {
|
||||
private ServiceHttpClientFactory() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static Builder newBuilder(@Nonnull Duration connectTimeout) {
|
||||
Objects.requireNonNull(connectTimeout, "connectTimeout");
|
||||
return HttpClient.newBuilder().connectTimeout(connectTimeout);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static HttpClient create(@Nonnull Duration connectTimeout) {
|
||||
return newBuilder(connectTimeout).build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user