mirror of
https://github.com/Samsuik/Sakura.git
synced 2025-12-23 00:39:20 +00:00
Start on feature patches
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samsuik <kfian294ma4@gmail.com>
|
||||
Date: Tue, 21 Sep 2021 23:54:25 +0100
|
||||
Subject: [PATCH] Client Visibility Settings API
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java
|
||||
index 1b5f3007a0afa7d007da84ba6afeb8b0185f6997..7dbfa59c02b0ce7d96796ff984d4a5879e9d6f95 100644
|
||||
--- a/src/main/java/org/bukkit/entity/Player.java
|
||||
+++ b/src/main/java/org/bukkit/entity/Player.java
|
||||
@@ -66,6 +66,14 @@ public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginM
|
||||
|
||||
void setTrackingRangeModifier(double mod);
|
||||
// Sakura end - entity tracking range modifier
|
||||
+ // Sakura start - client visibility settings api
|
||||
+ /**
|
||||
+ * Server-side api to disable sending visual effects to the client.
|
||||
+ *
|
||||
+ * @return visibility api
|
||||
+ */
|
||||
+ me.samsuik.sakura.player.visibility.VisibilitySettings getVisibility();
|
||||
+ // Sakura end - client visibility settings api
|
||||
|
||||
// Paper start
|
||||
@Override
|
||||
@@ -0,0 +1,32 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samsuik <kfian294ma4@gmail.com>
|
||||
Date: Sat, 9 Sep 2023 18:39:15 +0100
|
||||
Subject: [PATCH] Merge Cannon Entities API
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/entity/FallingBlock.java b/src/main/java/org/bukkit/entity/FallingBlock.java
|
||||
index 0f7d64c1c0221ef7fa933df8c5572dbfdcba5128..78d62651760ff0c9ebc5c01d2417c8c86e116df5 100644
|
||||
--- a/src/main/java/org/bukkit/entity/FallingBlock.java
|
||||
+++ b/src/main/java/org/bukkit/entity/FallingBlock.java
|
||||
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
/**
|
||||
* Represents a falling block
|
||||
*/
|
||||
-public interface FallingBlock extends Entity {
|
||||
+public interface FallingBlock extends Entity, me.samsuik.sakura.entity.merge.Mergeable { // Sakura - merge cannon entities api
|
||||
|
||||
// Sakura start - falling block height parity api
|
||||
/**
|
||||
diff --git a/src/main/java/org/bukkit/entity/TNTPrimed.java b/src/main/java/org/bukkit/entity/TNTPrimed.java
|
||||
index 87e717c9ea61b0cbf536bc62fa829ddcfae5ad8c..2e89ea4e896bdea552ec40fca927920f5f96fd59 100644
|
||||
--- a/src/main/java/org/bukkit/entity/TNTPrimed.java
|
||||
+++ b/src/main/java/org/bukkit/entity/TNTPrimed.java
|
||||
@@ -6,7 +6,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
/**
|
||||
* Represents a Primed TNT.
|
||||
*/
|
||||
-public interface TNTPrimed extends Explosive {
|
||||
+public interface TNTPrimed extends Explosive, me.samsuik.sakura.entity.merge.Mergeable { // Sakura - merge cannon entities api
|
||||
|
||||
/**
|
||||
* Set the number of ticks until the TNT blows up after being primed.
|
||||
@@ -0,0 +1,48 @@
|
||||
package me.samsuik.sakura.player.visibility;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface VisibilitySettings {
|
||||
default boolean isEnabled(VisibilityType type) {
|
||||
return this.get(type) == VisibilityState.ON;
|
||||
}
|
||||
|
||||
default boolean isDisabled(VisibilityType type) {
|
||||
return this.get(type) == VisibilityState.OFF;
|
||||
}
|
||||
|
||||
default boolean isToggled(VisibilityType type) {
|
||||
return !type.isDefault(this.get(type));
|
||||
}
|
||||
|
||||
default VisibilityState toggle(VisibilityType type) {
|
||||
VisibilityState state = this.get(type);
|
||||
return this.set(type, toggleState(state));
|
||||
}
|
||||
|
||||
default VisibilityState cycle(VisibilityType type) {
|
||||
VisibilityState state = this.get(type);
|
||||
return this.set(type, type.cycle(state));
|
||||
}
|
||||
|
||||
default void toggleAll() {
|
||||
VisibilityState state = this.currentState();
|
||||
VisibilityState newState = toggleState(state);
|
||||
for (VisibilityType type : VisibilityTypes.types()) {
|
||||
this.set(type, newState);
|
||||
}
|
||||
}
|
||||
|
||||
VisibilityState get(VisibilityType type);
|
||||
|
||||
VisibilityState set(VisibilityType type, VisibilityState state);
|
||||
|
||||
VisibilityState currentState();
|
||||
|
||||
boolean playerModified();
|
||||
|
||||
static VisibilityState toggleState(VisibilityState state) {
|
||||
return state != VisibilityState.OFF ? VisibilityState.OFF : VisibilityState.ON;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package me.samsuik.sakura.player.visibility;
|
||||
|
||||
public enum VisibilityState {
|
||||
ON, MODIFIED, MINIMAL, OFF;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package me.samsuik.sakura.player.visibility;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public record VisibilityType(String key, ImmutableList<VisibilityState> states) {
|
||||
public VisibilityState getDefault() {
|
||||
return this.states.getFirst();
|
||||
}
|
||||
|
||||
public boolean isDefault(VisibilityState state) {
|
||||
return state == this.getDefault();
|
||||
}
|
||||
|
||||
public VisibilityState cycle(VisibilityState state) {
|
||||
int index = this.states.indexOf(state);
|
||||
int next = (index + 1) % this.states.size();
|
||||
return this.states.get(next);
|
||||
}
|
||||
|
||||
public static VisibilityType from(String key, boolean minimal) {
|
||||
return new VisibilityType(key, states(minimal));
|
||||
}
|
||||
|
||||
private static ImmutableList<VisibilityState> states(boolean minimal) {
|
||||
ImmutableList.Builder<VisibilityState> listBuilder = ImmutableList.builder();
|
||||
listBuilder.add(VisibilityState.ON);
|
||||
if (minimal) {
|
||||
listBuilder.add(VisibilityState.MINIMAL);
|
||||
}
|
||||
listBuilder.add(VisibilityState.OFF);
|
||||
return listBuilder.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package me.samsuik.sakura.player.visibility;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@NullMarked
|
||||
public final class VisibilityTypes {
|
||||
private static final List<VisibilityType> TYPES = new ArrayList<>();
|
||||
|
||||
public static final VisibilityType TNT = register(create("tnt", true));
|
||||
public static final VisibilityType SAND = register(create("sand", true));
|
||||
public static final VisibilityType EXPLOSIONS = register(create("explosions", true));
|
||||
public static final VisibilityType SPAWNERS = register(create("spawners", false));
|
||||
public static final VisibilityType PISTONS = register(create("pistons", false));
|
||||
|
||||
public static ImmutableList<VisibilityType> types() {
|
||||
return ImmutableList.copyOf(TYPES);
|
||||
}
|
||||
|
||||
private static VisibilityType create(String key, boolean minimal) {
|
||||
return VisibilityType.from(key, minimal);
|
||||
}
|
||||
|
||||
private static VisibilityType register(VisibilityType type) {
|
||||
TYPES.add(type);
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samsuik <kfian294ma4@gmail.com>
|
||||
Date: Tue, 21 Sep 2021 23:54:25 +0100
|
||||
Subject: [PATCH] Client Visibility Settings
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 3a926d4d34a7c68a24b9e00bcbcff271c8992ad2..f1f322f623756f0c0a06a4207a12765c9623e151 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1749,6 +1749,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
profilerFiller.pop();
|
||||
profilerFiller.pop();
|
||||
serverLevel.explosionDensityCache.clear(); // Paper - Optimize explosions
|
||||
+ serverLevel.explosionPositions.clear(); // Sakura - client visibility settings
|
||||
}
|
||||
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
|
||||
|
||||
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
|
||||
index 8f9f759885a9cda57ae7d665ec309a57e12969fd..e096463443639e9eef5311d7154f6d2ac1517883 100644
|
||||
--- a/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -168,6 +168,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
this.handleLegacyStructureIndex(pos);
|
||||
}
|
||||
// Paper end - rewrite chunk system
|
||||
+ private final it.unimi.dsi.fastutil.longs.Long2IntMap minimalEntities; // Sakura - client visibility settings; minimal tnt/sand
|
||||
|
||||
public ChunkMap(
|
||||
ServerLevel level,
|
||||
@@ -230,6 +231,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
);
|
||||
this.setServerViewDistance(viewDistance);
|
||||
this.worldGenContext = new WorldGenContext(level, generator, structureManager, this.lightEngine, null, this::setChunkUnsaved); // Paper - rewrite chunk system
|
||||
+ // Sakura start - client visibility settings; minimal tnt/sand
|
||||
+ this.minimalEntities = new it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap();
|
||||
+ this.minimalEntities.defaultReturnValue(Integer.MIN_VALUE);
|
||||
+ // Sakura end - client visibility settings; minimal tnt/sand
|
||||
}
|
||||
|
||||
private void setChunkUnsaved(ChunkPos chunkPos) {
|
||||
@@ -954,6 +959,8 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
tracker.serverEntity.sendChanges();
|
||||
}
|
||||
}
|
||||
+
|
||||
+ this.minimalEntities.clear(); // Sakura - client visibility settings; minimal tnt/sand
|
||||
}
|
||||
// Paper end - optimise entity tracker
|
||||
|
||||
@@ -1164,6 +1171,32 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
return !this.seenBy.isEmpty();
|
||||
}
|
||||
// Paper end - optimise entity tracker
|
||||
+ // Sakura start - client visibility settings; entity visibility
|
||||
+ private boolean checkEntityVisibility(final ServerPlayer player) {
|
||||
+ final Entity entity = this.entity;
|
||||
+ final me.samsuik.sakura.player.visibility.VisibilitySettings settings = player.visibilitySettings;
|
||||
+ if (!settings.playerModified() || !(entity.isPrimedTNT || entity.isFallingBlock)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ final me.samsuik.sakura.player.visibility.VisibilityType type;
|
||||
+ if (entity.isPrimedTNT) {
|
||||
+ type = me.samsuik.sakura.player.visibility.VisibilityTypes.TNT;
|
||||
+ } else {
|
||||
+ type = me.samsuik.sakura.player.visibility.VisibilityTypes.SAND;
|
||||
+ }
|
||||
+ final me.samsuik.sakura.player.visibility.VisibilityState state = settings.get(type);
|
||||
+ if (state == me.samsuik.sakura.player.visibility.VisibilityState.MINIMAL) {
|
||||
+ final long key = entity.blockPosition().asLong() ^ entity.getType().hashCode();
|
||||
+ final long visibleEntity = ChunkMap.this.minimalEntities.get(key);
|
||||
+ if (visibleEntity != Integer.MIN_VALUE) {
|
||||
+ return entity.getId() == visibleEntity;
|
||||
+ } else {
|
||||
+ ChunkMap.this.minimalEntities.put(key, entity.getId());
|
||||
+ }
|
||||
+ }
|
||||
+ return state != me.samsuik.sakura.player.visibility.VisibilityState.OFF;
|
||||
+ }
|
||||
+ // Sakura end - client visibility settings; entity visibility
|
||||
|
||||
public TrackedEntity(final Entity entity, final int range, final int updateInterval, final boolean trackDelta) {
|
||||
this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, updateInterval, trackDelta, this::broadcast, this.seenBy); // CraftBukkit
|
||||
@@ -1234,6 +1267,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
}
|
||||
flag = flag && this.entity.broadcastToPlayer(player) && ChunkMap.this.isChunkTracked(player, this.entity.chunkPosition().x, this.entity.chunkPosition().z);
|
||||
// Paper end - Configurable entity tracking range by Y
|
||||
+ flag = flag && this.checkEntityVisibility(player); // Sakura start - client visibility settings; entity visibility
|
||||
// CraftBukkit start - respect vanish API
|
||||
if (flag && !player.getBukkitEntity().canSee(this.entity.getBukkitEntity())) { // Paper - only consider hits
|
||||
flag = false;
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index b49a0c3eb4236c71e390f42278412dce59e98cbd..30454e69d603c3de3e2ecb5498d79c25bcc15a07 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -569,6 +569,21 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
);
|
||||
}
|
||||
// Paper end - chunk tick iteration
|
||||
+ // Sakura start - client visibility settings
|
||||
+ public final LongSet explosionPositions = new it.unimi.dsi.fastutil.longs.LongOpenHashSet();
|
||||
+
|
||||
+ public final boolean checkExplosionVisibility(final Vec3 position, final ServerPlayer player) {
|
||||
+ final me.samsuik.sakura.player.visibility.VisibilitySettings settings = player.visibilitySettings;
|
||||
+ if (settings.isDisabled(me.samsuik.sakura.player.visibility.VisibilityTypes.EXPLOSIONS)) {
|
||||
+ return false;
|
||||
+ } else if (settings.isToggled(me.samsuik.sakura.player.visibility.VisibilityTypes.EXPLOSIONS)) {
|
||||
+ final BlockPos blockPosition = BlockPos.containing(position);
|
||||
+ final long encodedPosition = blockPosition.asLong();
|
||||
+ return this.explosionPositions.add(encodedPosition);
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Sakura end - client visibility settings
|
||||
|
||||
public ServerLevel(
|
||||
MinecraftServer server,
|
||||
@@ -1854,7 +1869,18 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
for (ServerPlayer serverPlayer : this.players) {
|
||||
if (serverPlayer.distanceToSqr(vec3) < 4096.0) {
|
||||
Optional<Vec3> optional = Optional.ofNullable(serverExplosion.getHitPlayers().get(serverPlayer));
|
||||
- serverPlayer.connection.send(new ClientboundExplodePacket(vec3, optional, particleOptions, explosionSound));
|
||||
+ // Sakura start - client visibility settings; let players toggle explosion particles
|
||||
+ ParticleOptions particle = particleOptions;
|
||||
+ Vec3 position = vec3;
|
||||
+ // In 1.22 and later this should be replaced with sending the motion through a PlayerPositionPacket.
|
||||
+ // The problem here is SetEntityMotion is capped to 3.9 b/pt and the only other alternate mean was
|
||||
+ // implemented in 1.21.3. I believe it's best to just wait on this issue and deal with this hack.
|
||||
+ if (!this.checkExplosionVisibility(vec3, serverPlayer)) {
|
||||
+ position = new Vec3(0.0, -1024.0, 0.0);
|
||||
+ particle = net.minecraft.core.particles.ParticleTypes.SMOKE;
|
||||
+ }
|
||||
+ serverPlayer.connection.send(new ClientboundExplodePacket(position, optional, particle, explosionSound));
|
||||
+ // Sakura end - client visibility settings; let players toggle explosion particles
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index 4535ea5fe788fc5c473d8289c031f2c9e56cfce6..92ee40a77f1b13c2c720cd41747206f82a735f9e 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -424,6 +424,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
}
|
||||
// Paper end - rewrite chunk system
|
||||
public double trackingRangeModifier = 1.0; // Sakura - entity tracking range modifier
|
||||
+ public final me.samsuik.sakura.player.visibility.PlayerVisibilitySettings visibilitySettings = new me.samsuik.sakura.player.visibility.PlayerVisibilitySettings(); // Sakura - client visibility settings
|
||||
|
||||
public ServerPlayer(MinecraftServer server, ServerLevel level, GameProfile gameProfile, ClientInformation clientInformation) {
|
||||
super(level, level.getSharedSpawnPos(), level.getSharedSpawnAngle(), gameProfile);
|
||||
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
index e71c1a564e5d4ac43460f89879ff709ee685706f..7d2fe5df38db1d492ae65aa72959200221cf32d5 100644
|
||||
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
@@ -51,6 +51,21 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
public final java.util.Map<java.util.UUID, net.kyori.adventure.resource.ResourcePackCallback> packCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - adventure resource pack callbacks
|
||||
private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit
|
||||
protected static final net.minecraft.resources.ResourceLocation MINECRAFT_BRAND = net.minecraft.resources.ResourceLocation.withDefaultNamespace("brand"); // Paper - Brand support
|
||||
+ // Sakura start - client visibility settings
|
||||
+ private @Nullable Packet<?> recreatePacket(final Packet<?> packet) {
|
||||
+ final me.samsuik.sakura.player.visibility.VisibilitySettings settings = this.player.visibilitySettings;
|
||||
+ if (packet instanceof net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket bedPacket) {
|
||||
+ if (settings.isToggled(me.samsuik.sakura.player.visibility.VisibilityTypes.SPAWNERS) && bedPacket.getType() == net.minecraft.world.level.block.entity.BlockEntityType.MOB_SPAWNER) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ } else if (packet instanceof net.minecraft.network.protocol.game.ClientboundBlockEventPacket bePacket) {
|
||||
+ if (settings.isToggled(me.samsuik.sakura.player.visibility.VisibilityTypes.PISTONS) && bePacket.getBlock() instanceof net.minecraft.world.level.block.piston.PistonBaseBlock) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+ return packet;
|
||||
+ }
|
||||
+ // Sakura end - client visibility settings
|
||||
|
||||
public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie, net.minecraft.server.level.ServerPlayer player) { // CraftBukkit
|
||||
this.server = server;
|
||||
@@ -287,6 +302,12 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
} else if (packet instanceof net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket defaultSpawnPositionPacket) {
|
||||
this.player.compassTarget = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(defaultSpawnPositionPacket.getPos(), this.getCraftPlayer().getWorld());
|
||||
}
|
||||
+ // Sakura start - client visibility settings
|
||||
+ if (this.player.visibilitySettings.playerModified()) {
|
||||
+ packet = this.recreatePacket(packet);
|
||||
+ if (packet == null) return;
|
||||
+ }
|
||||
+ // Sakura end - client visibility settings
|
||||
// CraftBukkit end
|
||||
if (packet.isTerminal()) {
|
||||
this.close();
|
||||
@@ -299,7 +320,10 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
} catch (Throwable var7) {
|
||||
CrashReport crashReport = CrashReport.forThrowable(var7, "Sending packet");
|
||||
CrashReportCategory crashReportCategory = crashReport.addCategory("Packet being sent");
|
||||
- crashReportCategory.setDetail("Packet class", () -> packet.getClass().getCanonicalName());
|
||||
+ // Sakura start - client visibility settings; packet has to be effectively final
|
||||
+ final Packet<?> packetFinal = packet;
|
||||
+ crashReportCategory.setDetail("Packet class", () -> packetFinal.getClass().getCanonicalName());
|
||||
+ // Sakura end - client visibility settings; packet has to be effectively final
|
||||
throw new ReportedException(crashReport);
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 27ef385a85b13ceb58e8d149849983107c539b31..c44c9399ddbb34408b255550a98f5c54ecdf6aff 100644
|
||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -3185,6 +3185,7 @@ public class ServerGamePacketListenerImpl
|
||||
|
||||
event.setCancelled(cancelled);
|
||||
AbstractContainerMenu oldContainer = this.player.containerMenu; // SPIGOT-1224
|
||||
+ me.samsuik.sakura.player.gui.FeatureGui.clickEvent(event); // Sakura - client visibility settings
|
||||
this.cserver.getPluginManager().callEvent(event);
|
||||
if (this.player.containerMenu != oldContainer) {
|
||||
return;
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 54a3ac38b70a9733173fb2ce4a7f86b534de1136..c4ace72f2eaf6a15c98490d18d3478b9ad5ed1a6 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -526,6 +526,10 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
}
|
||||
// Paper end - optimise entity tracker
|
||||
public boolean pushedByFluid = true; // Sakura - entity pushed by fluid api
|
||||
+ // Sakura start - client visibility settings
|
||||
+ public boolean isPrimedTNT;
|
||||
+ public boolean isFallingBlock;
|
||||
+ // Sakura end - client visibility settings
|
||||
|
||||
public Entity(EntityType<?> entityType, Level level) {
|
||||
this.type = entityType;
|
||||
diff --git a/net/minecraft/world/entity/item/FallingBlockEntity.java b/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
index 6b708503367f989665b89a39d367935c80210d1d..5462a423be1317a306ca12ed10edcab636cd0c7f 100644
|
||||
--- a/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
+++ b/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
@@ -73,6 +73,7 @@ public class FallingBlockEntity extends Entity {
|
||||
public FallingBlockEntity(EntityType<? extends FallingBlockEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.heightParity = level.sakuraConfig().cannons.mechanics.fallingBlockParity; // Sakura - configure cannon mechanics
|
||||
+ this.isFallingBlock = true; // Sakura - client visibility settings
|
||||
}
|
||||
|
||||
public FallingBlockEntity(Level level, double x, double y, double z, BlockState state) {
|
||||
diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
index 3c673e5b8a52e322d41ed442ed06337cacb58771..8118911019f7fc81218a656e1ecbd7eada505741 100644
|
||||
--- a/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
+++ b/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
@@ -61,6 +61,7 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
||||
public PrimedTnt(EntityType<? extends PrimedTnt> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.blocksBuilding = true;
|
||||
+ this.isPrimedTNT = true; // Sakura - client visibility settings
|
||||
}
|
||||
|
||||
public PrimedTnt(Level level, double x, double y, double z, @Nullable LivingEntity owner) {
|
||||
@@ -0,0 +1,148 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samsuik <kfian294ma4@gmail.com>
|
||||
Date: Sat, 11 Sep 2021 19:19:41 +0100
|
||||
Subject: [PATCH] Load Chunks on Movement
|
||||
|
||||
|
||||
diff --git a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
||||
index e04bd54744335fb5398c6e4f7ce8b981f35bfb7d..651a45b795818bd7b1364b95c52570fd99dd35e4 100644
|
||||
--- a/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
||||
+++ b/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java
|
||||
@@ -1885,6 +1885,7 @@ public final class CollisionUtil {
|
||||
public static final int COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS = 1 << 1;
|
||||
public static final int COLLISION_FLAG_CHECK_BORDER = 1 << 2;
|
||||
public static final int COLLISION_FLAG_CHECK_ONLY = 1 << 3;
|
||||
+ public static final int COLLISION_FLAG_ADD_TICKET = 1 << 4; // Sakura - load chunks on movement
|
||||
|
||||
public static boolean getCollisionsForBlocksOrWorldBorder(final Level world, final Entity entity, final AABB aabb,
|
||||
final List<VoxelShape> intoVoxel, final List<AABB> intoAABB,
|
||||
@@ -1936,6 +1937,7 @@ public final class CollisionUtil {
|
||||
final int maxChunkZ = maxBlockZ >> 4;
|
||||
|
||||
final boolean loadChunks = (collisionFlags & COLLISION_FLAG_LOAD_CHUNKS) != 0;
|
||||
+ final boolean addTicket = (collisionFlags & COLLISION_FLAG_ADD_TICKET) != 0; // Sakura - load chunks on movement
|
||||
final ChunkSource chunkSource = world.getChunkSource();
|
||||
|
||||
for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
|
||||
@@ -1954,6 +1956,13 @@ public final class CollisionUtil {
|
||||
continue;
|
||||
}
|
||||
|
||||
+ // Sakura start - load chunks on movement
|
||||
+ if (addTicket && chunk.movementTicketNeedsUpdate() && chunkSource instanceof net.minecraft.server.level.ServerChunkCache chunkCache) {
|
||||
+ final long chunkKey = ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkKey(currChunkX, currChunkZ);
|
||||
+ chunkCache.chunkMap.getDistanceManager().moonrise$getChunkHolderManager().addTicketAtLevel(net.minecraft.server.level.TicketType.ENTITY_MOVEMENT, currChunkX, currChunkZ, 31, chunkKey);
|
||||
+ chunk.updatedMovementTicket();
|
||||
+ }
|
||||
+ // Sakura end - load chunks on movement
|
||||
final LevelChunkSection[] sections = chunk.getSections();
|
||||
|
||||
// bound y
|
||||
diff --git a/net/minecraft/server/level/TicketType.java b/net/minecraft/server/level/TicketType.java
|
||||
index 8f12a4df5d63ecd11e6e615d910b6e3f6dde5f3c..56beffa0c5cdb0d6a4836a0ee496bd638432b143 100644
|
||||
--- a/net/minecraft/server/level/TicketType.java
|
||||
+++ b/net/minecraft/server/level/TicketType.java
|
||||
@@ -21,6 +21,7 @@ public class TicketType<T> {
|
||||
public static final TicketType<Unit> PLUGIN = TicketType.create("plugin", (a, b) -> 0); // CraftBukkit
|
||||
public static final TicketType<org.bukkit.plugin.Plugin> PLUGIN_TICKET = TicketType.create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit
|
||||
public static final TicketType<Integer> POST_TELEPORT = TicketType.create("post_teleport", Integer::compare, 5); // Paper - post teleport ticket type
|
||||
+ public static final TicketType<Long> ENTITY_MOVEMENT = TicketType.create("entity_movement", Long::compareTo, 10*20); // Sakura - load chunks on movement
|
||||
|
||||
public static <T> TicketType<T> create(String name, Comparator<T> comparator) {
|
||||
return new TicketType<>(name, comparator, 0L);
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index c4ace72f2eaf6a15c98490d18d3478b9ad5ed1a6..00b3e16d5465547e7d4f8126664fb7eda3b3c568 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -530,6 +530,20 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
public boolean isPrimedTNT;
|
||||
public boolean isFallingBlock;
|
||||
// Sakura end - client visibility settings
|
||||
+ // Sakura start - load chunks on movement
|
||||
+ protected boolean loadChunks = false;
|
||||
+
|
||||
+ private int getExtraCollisionFlags() {
|
||||
+ int flags = 0;
|
||||
+
|
||||
+ if (this.loadChunks) {
|
||||
+ flags |= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_LOAD_CHUNKS;
|
||||
+ flags |= ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_ADD_TICKET;
|
||||
+ }
|
||||
+
|
||||
+ return flags;
|
||||
+ }
|
||||
+ // Sakura end - load chunks on movement
|
||||
|
||||
public Entity(EntityType<?> entityType, Level level) {
|
||||
this.type = entityType;
|
||||
@@ -1469,7 +1483,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
|
||||
ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.getCollisionsForBlocksOrWorldBorder(
|
||||
this.level, (Entity)(Object)this, initialCollisionBox, potentialCollisionsVoxel, potentialCollisionsBB,
|
||||
- ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER, null
|
||||
+ ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.COLLISION_FLAG_CHECK_BORDER | this.getExtraCollisionFlags(), null // Sakura - load chunks on movement
|
||||
);
|
||||
potentialCollisionsBB.addAll(entityAABBs);
|
||||
final Vec3 collided = ca.spottedleaf.moonrise.patches.collisions.CollisionUtil.performCollisions(movement, currentBox, potentialCollisionsVoxel, potentialCollisionsBB);
|
||||
@@ -4961,13 +4975,14 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
@Override
|
||||
public boolean shouldBeSaved() {
|
||||
return (this.removalReason == null || this.removalReason.shouldSave())
|
||||
+ && !this.loadChunks // Sakura - load chunks on movement; this is used to check if the chunk the entity is in can be unloaded
|
||||
&& !this.isPassenger()
|
||||
&& (!this.isVehicle() || !((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity)this).moonrise$hasAnyPlayerPassengers()); // Paper - rewrite chunk system
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlwaysTicking() {
|
||||
- return false;
|
||||
+ return this.loadChunks; // Sakura - load chunks on movement; always tick in unloaded & lazy chunks
|
||||
}
|
||||
|
||||
public boolean mayInteract(ServerLevel level, BlockPos pos) {
|
||||
diff --git a/net/minecraft/world/entity/item/FallingBlockEntity.java b/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
index 5462a423be1317a306ca12ed10edcab636cd0c7f..ee5ec7a780488182e30134b29a20cc192609d64b 100644
|
||||
--- a/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
+++ b/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
@@ -74,6 +74,7 @@ public class FallingBlockEntity extends Entity {
|
||||
super(entityType, level);
|
||||
this.heightParity = level.sakuraConfig().cannons.mechanics.fallingBlockParity; // Sakura - configure cannon mechanics
|
||||
this.isFallingBlock = true; // Sakura - client visibility settings
|
||||
+ this.loadChunks = level.sakuraConfig().cannons.loadChunks; // Sakura - load chunks on movement
|
||||
}
|
||||
|
||||
public FallingBlockEntity(Level level, double x, double y, double z, BlockState state) {
|
||||
diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
index 8118911019f7fc81218a656e1ecbd7eada505741..e7b4efe35c20e11f130b5bce5c8c20390c65e0a4 100644
|
||||
--- a/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
+++ b/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
@@ -62,6 +62,7 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
||||
super(entityType, level);
|
||||
this.blocksBuilding = true;
|
||||
this.isPrimedTNT = true; // Sakura - client visibility settings
|
||||
+ this.loadChunks = level.sakuraConfig().cannons.loadChunks; // Sakura - load chunks on movement
|
||||
}
|
||||
|
||||
public PrimedTnt(Level level, double x, double y, double z, @Nullable LivingEntity owner) {
|
||||
diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
index 6d565b52552534ce9cacfc35ad1bf4adcb69eac3..9b42bd1afb9a6c1729cb56e3c232f46112ba57d3 100644
|
||||
--- a/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
+++ b/net/minecraft/world/level/chunk/ChunkAccess.java
|
||||
@@ -138,6 +138,17 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh
|
||||
private final int minSection;
|
||||
private final int maxSection;
|
||||
// Paper end - get block chunk optimisation
|
||||
+ // Sakura start - load chunks on movement
|
||||
+ private long lastMovementLoadTicket = 0;
|
||||
+
|
||||
+ public final boolean movementTicketNeedsUpdate() {
|
||||
+ return net.minecraft.server.MinecraftServer.currentTick - this.lastMovementLoadTicket >= 100;
|
||||
+ }
|
||||
+
|
||||
+ public final void updatedMovementTicket() {
|
||||
+ this.lastMovementLoadTicket = net.minecraft.server.MinecraftServer.currentTick;
|
||||
+ }
|
||||
+ // Sakura end - load chunks on movement
|
||||
|
||||
public ChunkAccess(
|
||||
ChunkPos chunkPos,
|
||||
@@ -0,0 +1,206 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Cryptite <cryptite@gmail.com>
|
||||
Date: Wed, 6 Oct 2021 11:03:01 -0500
|
||||
Subject: [PATCH] (Slice) Packet obfuscation and reduction
|
||||
|
||||
Minecraft is overzealous about packet updates for Entities. In Loka's case, we want to reduce as many unnecessary
|
||||
packet updates as possible. This patch is likely to be updated over and over in terms of reducing packet sends.
|
||||
|
||||
In summary, this patch creates the concept of a "foreignValue" of a packet's data. We treat packets in two ways:
|
||||
1) The packet sent to the player itself (the normal way). This always has all of the values as usual.
|
||||
2) The packet data as seen by any other (foreign) players.
|
||||
|
||||
This patch adds the ability to set a "foreignValue" for an entity value so as to obfuscate data received by other players.
|
||||
The current packets modified/obfuscated are the following:
|
||||
|
||||
# This reduces the amount of health packet updates as well which is great for players in combat.
|
||||
|
||||
# Air level packets are sent PER-TICK, and as such a player with any change in air level will only spam themselves
|
||||
# with packets instead of every single player within tracking distance
|
||||
|
||||
diff --git a/net/minecraft/network/syncher/SynchedEntityData.java b/net/minecraft/network/syncher/SynchedEntityData.java
|
||||
index 3d90f9f1ac1bd281edf6bb0f93ea821657d5bd2f..6f3a6efe6624f9d4e500b2eee5d8aed3a6077e71 100644
|
||||
--- a/net/minecraft/network/syncher/SynchedEntityData.java
|
||||
+++ b/net/minecraft/network/syncher/SynchedEntityData.java
|
||||
@@ -20,6 +20,30 @@ public class SynchedEntityData {
|
||||
private final SyncedDataHolder entity;
|
||||
private final SynchedEntityData.DataItem<?>[] itemsById;
|
||||
private boolean isDirty;
|
||||
+ // Slice start - packet obfuscation and reduction
|
||||
+ private boolean isForeignDirty;
|
||||
+
|
||||
+ public final boolean isForeignDirty() {
|
||||
+ return this.isForeignDirty;
|
||||
+ }
|
||||
+
|
||||
+ @Nullable
|
||||
+ public final List<SynchedEntityData.DataValue<?>> packForeignDirty(List<DataValue<?>> unpackedData) {
|
||||
+ List<SynchedEntityData.DataValue<?>> list = null;
|
||||
+ for (DataValue<?> dataItem : unpackedData) {
|
||||
+ DataItem<?> item = this.itemsById[dataItem.id()];
|
||||
+ if (item.isDirty(true)) {
|
||||
+ item.setForeignDirty(false);
|
||||
+ if (list == null) {
|
||||
+ list = new ArrayList<>();
|
||||
+ }
|
||||
+ list.add(item.copy(true));
|
||||
+ }
|
||||
+ }
|
||||
+ this.isForeignDirty = false;
|
||||
+ return list;
|
||||
+ }
|
||||
+ // Slice end - packet obfuscation and reduction
|
||||
|
||||
SynchedEntityData(SyncedDataHolder entity, SynchedEntityData.DataItem<?>[] itemsById) {
|
||||
this.entity = entity;
|
||||
@@ -58,6 +82,16 @@ public class SynchedEntityData {
|
||||
}
|
||||
|
||||
public <T> void set(EntityDataAccessor<T> key, T value, boolean force) {
|
||||
+ // Slice start - packet obfuscation and reduction
|
||||
+ this.set(key, value, null, force);
|
||||
+ }
|
||||
+
|
||||
+ public <T> void set(EntityDataAccessor<T> key, T value, T foreignValue) {
|
||||
+ this.set(key, value, foreignValue, false);
|
||||
+ }
|
||||
+
|
||||
+ public <T> void set(EntityDataAccessor<T> key, T value, T foreignValue, boolean force) {
|
||||
+ // Slice end - packet obfuscation and reduction
|
||||
SynchedEntityData.DataItem<T> item = this.getItem(key);
|
||||
if (force || ObjectUtils.notEqual(value, item.getValue())) {
|
||||
item.setValue(value);
|
||||
@@ -65,6 +99,12 @@ public class SynchedEntityData {
|
||||
item.setDirty(true);
|
||||
this.isDirty = true;
|
||||
}
|
||||
+ // Slice start - packet obfuscation and reduction
|
||||
+ if (foreignValue != null && ObjectUtils.notEqual(foreignValue, item.getForeignValue())) {
|
||||
+ item.setForeignValue(foreignValue);
|
||||
+ this.isForeignDirty = true;
|
||||
+ }
|
||||
+ // Slice end - packet obfuscation and reduction
|
||||
}
|
||||
|
||||
// CraftBukkit start - add method from above
|
||||
@@ -195,6 +235,38 @@ public class SynchedEntityData {
|
||||
T value;
|
||||
private final T initialValue;
|
||||
private boolean dirty;
|
||||
+ // Slice start - packet obfuscation and reduction
|
||||
+ @Nullable T foreignValue = null;
|
||||
+ private boolean foreignDirty = true;
|
||||
+
|
||||
+ public final void setForeignValue(T foreignValue) {
|
||||
+ this.foreignValue = foreignValue;
|
||||
+ this.foreignDirty = true;
|
||||
+ }
|
||||
+
|
||||
+ public final @Nullable T getForeignValue() {
|
||||
+ return this.foreignValue;
|
||||
+ }
|
||||
+
|
||||
+ public final boolean isDirty(boolean foreign) {
|
||||
+ if (foreign) {
|
||||
+ //There must be a foreign value in order for this to be dirty, otherwise we consider this a normal
|
||||
+ //value and check the normal dirty flag.
|
||||
+ return this.foreignValue == null || this.foreignDirty;
|
||||
+ }
|
||||
+
|
||||
+ return this.dirty;
|
||||
+ }
|
||||
+
|
||||
+ public final void setForeignDirty(boolean dirty) {
|
||||
+ this.foreignDirty = dirty;
|
||||
+ }
|
||||
+
|
||||
+ public final SynchedEntityData.DataValue<T> copy(boolean foreign) {
|
||||
+ return SynchedEntityData.DataValue.create(this.accessor, this.accessor.serializer()
|
||||
+ .copy(foreign && this.foreignValue != null ? this.foreignValue : this.value));
|
||||
+ }
|
||||
+ // Slice end - packet obfuscation and reduction
|
||||
|
||||
public DataItem(EntityDataAccessor<T> accessor, T value) {
|
||||
this.accessor = accessor;
|
||||
diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java
|
||||
index 0fb253aa55a24b56b17f524b3261c5b75c7d7e59..8abe899d19434ad4c7cc6c1596bab16df7b14275 100644
|
||||
--- a/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -138,7 +138,7 @@ public class ServerEntity {
|
||||
this.sendDirtyEntityData();
|
||||
}
|
||||
|
||||
- if (this.forceStateResync || this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isDirty()) { // Paper - fix desync when a player is added to the tracker
|
||||
+ if (this.forceStateResync || this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isForeignDirty()) { // Slice - packet obfuscation and reduction // Paper - fix desync when a player is added to the tracker
|
||||
byte b = Mth.packDegrees(this.entity.getYRot());
|
||||
byte b1 = Mth.packDegrees(this.entity.getXRot());
|
||||
boolean flag = Math.abs(b - this.lastSentYRot) >= 1 || Math.abs(b1 - this.lastSentXRot) >= 1;
|
||||
@@ -404,7 +404,15 @@ public class ServerEntity {
|
||||
List<SynchedEntityData.DataValue<?>> list = entityData.packDirty();
|
||||
if (list != null) {
|
||||
this.trackedDataValues = entityData.getNonDefaultValues();
|
||||
- this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list));
|
||||
+ // Slice start - packet obfuscation and reduction
|
||||
+ if (!(this.entity instanceof ServerPlayer)) {
|
||||
+ list = entityData.packForeignDirty(list);
|
||||
+ }
|
||||
+
|
||||
+ if (list != null) {
|
||||
+ this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list));
|
||||
+ }
|
||||
+ // Slice end - packet obfuscation and reduction
|
||||
}
|
||||
|
||||
if (this.entity instanceof LivingEntity) {
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 00b3e16d5465547e7d4f8126664fb7eda3b3c568..7bbc4a982f442fdb9821221442737ae65e55289e 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -3495,7 +3495,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
this.entityData.markDirty(Entity.DATA_AIR_SUPPLY_ID);
|
||||
return;
|
||||
}
|
||||
- this.entityData.set(Entity.DATA_AIR_SUPPLY_ID, event.getAmount());
|
||||
+ this.entityData.set(Entity.DATA_AIR_SUPPLY_ID, event.getAmount(), getMaxAirSupply()); // Slice
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/entity/item/FallingBlockEntity.java b/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
index ee5ec7a780488182e30134b29a20cc192609d64b..4836be29ff5e87e4f2b9beb0d4a9943281ab5262 100644
|
||||
--- a/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
+++ b/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
@@ -120,7 +120,7 @@ public class FallingBlockEntity extends Entity {
|
||||
}
|
||||
|
||||
public void setStartPos(BlockPos startPos) {
|
||||
- this.entityData.set(DATA_START_POS, startPos);
|
||||
+ this.entityData.set(DATA_START_POS, startPos, BlockPos.ZERO); // Slice - packet obfuscation and reduction
|
||||
}
|
||||
|
||||
public BlockPos getStartPos() {
|
||||
diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
index e7b4efe35c20e11f130b5bce5c8c20390c65e0a4..9e9463d62aa1618a4a749bb7e2636c9b090991e9 100644
|
||||
--- a/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
+++ b/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
@@ -241,7 +241,7 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
||||
}
|
||||
|
||||
public void setFuse(int life) {
|
||||
- this.entityData.set(DATA_FUSE_ID, life);
|
||||
+ this.entityData.set(DATA_FUSE_ID, life, (life / 10) * 10); // Slice - packet obfuscation and reduction
|
||||
}
|
||||
|
||||
public int getFuse() {
|
||||
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
|
||||
index ee6d5d3ccd55cffdeb96eeb77f648fd82feceb80..09209e5a9b61244a0877818c0841d19aee72522b 100644
|
||||
--- a/net/minecraft/world/entity/player/Player.java
|
||||
+++ b/net/minecraft/world/entity/player/Player.java
|
||||
@@ -673,7 +673,7 @@ public abstract class Player extends LivingEntity {
|
||||
|
||||
public void increaseScore(int score) {
|
||||
int score1 = this.getScore();
|
||||
- this.entityData.set(DATA_SCORE_ID, score1 + score);
|
||||
+ this.entityData.set(DATA_SCORE_ID, score1 + score, 0); // Slice - packet obfuscation and reduction
|
||||
}
|
||||
|
||||
public void startAutoSpinAttack(int ticks, float damage, ItemStack itemStack) {
|
||||
@@ -0,0 +1,160 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samsuik <kfian294ma4@gmail.com>
|
||||
Date: Fri, 19 Apr 2024 22:20:03 +0100
|
||||
Subject: [PATCH] Optimise paper explosions
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
|
||||
index df584256e133bfc0b7effd56961f1b91c264c7bd..9f06e32101f494d94d9210210255d5d72ca4ff36 100644
|
||||
--- a/net/minecraft/world/level/ServerExplosion.java
|
||||
+++ b/net/minecraft/world/level/ServerExplosion.java
|
||||
@@ -88,7 +88,7 @@ public class ServerExplosion implements Explosion {
|
||||
}
|
||||
}
|
||||
|
||||
- CACHED_RAYS = rayCoords.toDoubleArray();
|
||||
+ CACHED_RAYS = sortExplosionRays(rayCoords); // Sakura - optimise paper explosions
|
||||
}
|
||||
|
||||
private static final int CHUNK_CACHE_SHIFT = 2;
|
||||
@@ -307,6 +307,39 @@ public class ServerExplosion implements Explosion {
|
||||
}
|
||||
// Paper end - collisions optimisations
|
||||
private final boolean consistentRadius; // Sakura - consistent explosion radius
|
||||
+ // Sakura start - optimise paper explosions
|
||||
+ /*
|
||||
+ * Sort the explosion rays to better utilise the chunk and block cache.
|
||||
+ * x + Vanilla Sorted
|
||||
+ * z @ z 8 5
|
||||
+ * - x 6 7 6 4
|
||||
+ * 4 @ 5 7 @ 3
|
||||
+ * 2 3 8 2
|
||||
+ * 1 1
|
||||
+ */
|
||||
+ private static double[] sortExplosionRays(it.unimi.dsi.fastutil.doubles.DoubleArrayList rayCoords) {
|
||||
+ List<double[]> explosionRays = new ArrayList<>();
|
||||
+
|
||||
+ for (int i = 0; i < rayCoords.size(); i += 3) {
|
||||
+ double[] ray = new double[3];
|
||||
+ rayCoords.getElements(i, ray, 0, 3);
|
||||
+ explosionRays.add(ray);
|
||||
+ }
|
||||
+
|
||||
+ rayCoords.clear();
|
||||
+ explosionRays.sort(java.util.Comparator.comparingDouble(vec -> {
|
||||
+ double sign = Math.signum(vec[0]);
|
||||
+ double dir = (sign - 1) / 2;
|
||||
+ return sign + 8 + vec[2] * dir;
|
||||
+ }));
|
||||
+
|
||||
+ double[] rays = new double[explosionRays.size() * 3];
|
||||
+ for (int i = 0; i < explosionRays.size() * 3; i++) {
|
||||
+ rays[i] = explosionRays.get(i / 3)[i % 3];
|
||||
+ }
|
||||
+ return rays;
|
||||
+ }
|
||||
+ // Sakura end - optimise paper explosions
|
||||
|
||||
public ServerExplosion(
|
||||
ServerLevel level,
|
||||
@@ -398,6 +431,12 @@ public class ServerExplosion implements Explosion {
|
||||
initialCache = this.getOrCacheExplosionBlock(blockX, blockY, blockZ, key, true);
|
||||
}
|
||||
|
||||
+ // Sakura start - optimise paper explosions
|
||||
+ if (!this.interactsWithBlocks() || initialCache.resistance > (this.radius * 1.3f)) {
|
||||
+ return ret;
|
||||
+ }
|
||||
+ // Sakura end - optimise paper explosions
|
||||
+
|
||||
// only ~1/3rd of the loop iterations in vanilla will result in a ray, as it is iterating the perimeter of
|
||||
// a 16x16x16 cube
|
||||
// we can cache the rays and their normals as well, so that we eliminate the excess iterations / checks and
|
||||
@@ -477,16 +516,55 @@ public class ServerExplosion implements Explosion {
|
||||
// Paper end - collision optimisations
|
||||
}
|
||||
|
||||
- private void hurtEntities() {
|
||||
- float f = this.radius * 2.0F;
|
||||
+ // Sakura start - optimise paper explosions
|
||||
+ protected final AABB getExplosionBounds(float f) {
|
||||
int floor = Mth.floor(this.center.x - f - 1.0);
|
||||
int floor1 = Mth.floor(this.center.x + f + 1.0);
|
||||
int floor2 = Mth.floor(this.center.y - f - 1.0);
|
||||
int floor3 = Mth.floor(this.center.y + f + 1.0);
|
||||
int floor4 = Mth.floor(this.center.z - f - 1.0);
|
||||
int floor5 = Mth.floor(this.center.z + f + 1.0);
|
||||
- List <Entity> list = this.level.getEntities(excludeSourceFromDamage ? this.source : null, new AABB(floor, floor2, floor4, floor1, floor3, floor5), entity -> entity.isAlive() && !entity.isSpectator()); // Paper - Fix lag from explosions processing dead entities, Allow explosions to damage source
|
||||
- for (Entity entity : list) { // Paper - used in loop
|
||||
+ return new AABB(floor, floor2, floor4, floor1, floor3, floor5);
|
||||
+ }
|
||||
+
|
||||
+ private void hurtEntities() {
|
||||
+ float f = this.radius * 2.0F;
|
||||
+
|
||||
+ int minSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMinSection(this.level);
|
||||
+ int maxSection = ca.spottedleaf.moonrise.common.util.WorldUtil.getMaxSection(this.level);
|
||||
+
|
||||
+ int minChunkX = Mth.floor(this.center.x - f) >> 4;
|
||||
+ int maxChunkX = Mth.floor(this.center.x + f) >> 4;
|
||||
+ int minChunkY = Mth.clamp(Mth.floor(this.center.y - f) >> 4, minSection, maxSection);
|
||||
+ int maxChunkY = Mth.clamp(Mth.floor(this.center.y + f) >> 4, minSection, maxSection);
|
||||
+ int minChunkZ = Mth.floor(this.center.z - f) >> 4;
|
||||
+ int maxChunkZ = Mth.floor(this.center.z + f) >> 4;
|
||||
+
|
||||
+ ca.spottedleaf.moonrise.patches.chunk_system.level.entity.EntityLookup entityLookup = this.level.moonrise$getEntityLookup();
|
||||
+ for (int chunkX = minChunkX; chunkX <= maxChunkX; ++chunkX) {
|
||||
+ for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; ++chunkZ) {
|
||||
+ ca.spottedleaf.moonrise.patches.chunk_system.level.entity.ChunkEntitySlices chunk = entityLookup.getChunk(chunkX, chunkZ);
|
||||
+ if (chunk == null) continue; // empty slice
|
||||
+
|
||||
+ for (int chunkY = minChunkY; chunkY <= maxChunkY; ++chunkY) {
|
||||
+ this.impactEntities(f, chunk.getSectionEntities(chunkY));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ protected final void impactEntities(float f, Entity[] entities) {
|
||||
+ for (int i = 0; i < entities.length; i++) {
|
||||
+ Entity entity = entities[i];
|
||||
+ if (entity == null) break; // end of entity section
|
||||
+ this.impactEntity(f, entity);
|
||||
+ if (entity != entities[i]) i--; // entities can be removed mid-explosion
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ protected final void impactEntity(float f, Entity entity) {
|
||||
+ if (entity.isAlive() && !entity.isSpectator() && (!this.excludeSourceFromDamage || entity != this.source)) { // Paper - Fix lag from explosions processing dead entities, Allow explosions to damage source
|
||||
+ // Sakura end - optimise paper explosions
|
||||
if (!entity.ignoreExplosion(this)) {
|
||||
double d = Math.sqrt(entity.distanceToSqr(this.center)) / f;
|
||||
if (d <= 1.0) {
|
||||
@@ -511,15 +589,16 @@ public class ServerExplosion implements Explosion {
|
||||
// - Damaging EntityEnderDragon does nothing
|
||||
// - EnderDragon hitbock always covers the other parts and is therefore always present
|
||||
if (entity instanceof EnderDragonPart) {
|
||||
- continue;
|
||||
+ return; // Sakura - optimise paper explosions
|
||||
}
|
||||
|
||||
entity.lastDamageCancelled = false;
|
||||
|
||||
if (entity instanceof EnderDragon) {
|
||||
+ final AABB bounds = this.getExplosionBounds(f); // Sakura - optimise paper explosions
|
||||
for (EnderDragonPart dragonPart : ((EnderDragon) entity).getSubEntities()) {
|
||||
// Calculate damage separately for each EntityComplexPart
|
||||
- if (list.contains(dragonPart)) {
|
||||
+ if (dragonPart.getBoundingBox().intersects(bounds)) { // Sakura - optimise paper explosions
|
||||
dragonPart.hurtServer(this.level, this.damageSource, this.damageCalculator.getEntityDamageAmount(this, entity, f1));
|
||||
}
|
||||
}
|
||||
@@ -528,7 +607,7 @@ public class ServerExplosion implements Explosion {
|
||||
}
|
||||
|
||||
if (entity.lastDamageCancelled) { // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Skip entity if damage event was cancelled
|
||||
- continue;
|
||||
+ return; // Sakura - optimise paper explosions
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samsuik <kfian294ma4@gmail.com>
|
||||
Date: Wed, 16 Aug 2023 22:34:49 +0100
|
||||
Subject: [PATCH] Store Entity Data/State
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 7bbc4a982f442fdb9821221442737ae65e55289e..3671003b9fdf787fc5095d12df9ee2f15bd998f1 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -544,6 +544,25 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
return flags;
|
||||
}
|
||||
// Sakura end - load chunks on movement
|
||||
+ // Sakura start - store entity data/state
|
||||
+ private me.samsuik.sakura.entity.EntityState entityState = null;
|
||||
+
|
||||
+ public final Vec3 stuckSpeedMultiplier() {
|
||||
+ return this.stuckSpeedMultiplier;
|
||||
+ }
|
||||
+
|
||||
+ public final void storeEntityState() {
|
||||
+ this.entityState = me.samsuik.sakura.entity.EntityState.of(this);
|
||||
+ }
|
||||
+
|
||||
+ public final me.samsuik.sakura.entity.EntityState entityState() {
|
||||
+ return this.entityState;
|
||||
+ }
|
||||
+
|
||||
+ public final boolean compareState(Entity to) {
|
||||
+ return to.entityState() != null && to.entityState().comparePositionAndMotion(this);
|
||||
+ }
|
||||
+ // Sakura end - store entity data/state
|
||||
|
||||
public Entity(EntityType<?> entityType, Level level) {
|
||||
this.type = entityType;
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index 3ba2228fd83bcd9af5f46d71409abe9fd2328f7e..e4584cd58d84f2b64748dfec7a1aa69fca119021 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -1510,6 +1510,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
|
||||
public <T extends Entity> void guardEntityTick(Consumer<T> consumerEntity, T entity) {
|
||||
try {
|
||||
+ entity.storeEntityState(); // Sakura - store entity data/state
|
||||
consumerEntity.accept(entity);
|
||||
} catch (Throwable var6) {
|
||||
// Paper start - Prevent block entity and entity crashes
|
||||
@@ -0,0 +1,343 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samsuik <kfian294ma4@gmail.com>
|
||||
Date: Sat, 9 Sep 2023 18:39:15 +0100
|
||||
Subject: [PATCH] Merge Cannon Entities
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index f1f322f623756f0c0a06a4207a12765c9623e151..ae1fad06d85de83f53884449cff21fc0ae62bf97 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1750,6 +1750,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
profilerFiller.pop();
|
||||
serverLevel.explosionDensityCache.clear(); // Paper - Optimize explosions
|
||||
serverLevel.explosionPositions.clear(); // Sakura - client visibility settings
|
||||
+ serverLevel.mergeHandler.expire(currentTick); // Sakura - merge cannon entities
|
||||
}
|
||||
this.isIteratingOverLevels = false; // Paper - Throw exception on world create while being ticked
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index 30454e69d603c3de3e2ecb5498d79c25bcc15a07..83bd1ddec7edb09e9a28eead178d7d07cbde8749 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -810,6 +810,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
|
||||
io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR
|
||||
+ final Entity[] previousEntity = new Entity[1]; // Sakura - merge cannon entities
|
||||
this.entityTickList
|
||||
.forEach(
|
||||
entity -> {
|
||||
@@ -828,6 +829,15 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
entity.stopRiding();
|
||||
}
|
||||
|
||||
+ // Sakura start - merge cannon entities
|
||||
+ Entity previous = previousEntity[0];
|
||||
+ if (this.mergeHandler.tryMerge(entity, previous)) {
|
||||
+ return;
|
||||
+ } else {
|
||||
+ previousEntity[0] = entity;
|
||||
+ }
|
||||
+ // Sakura end - merge cannon entities
|
||||
+
|
||||
profilerFiller.push("tick");
|
||||
this.guardEntityTick(this::tickNonPassenger, entity);
|
||||
profilerFiller.pop();
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index 3671003b9fdf787fc5095d12df9ee2f15bd998f1..2a617bd6d5d14cd69b149d6c5f82f8b2c3bc2d5d 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -563,6 +563,23 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
return to.entityState() != null && to.entityState().comparePositionAndMotion(this);
|
||||
}
|
||||
// Sakura end - store entity data/state
|
||||
+ // Sakura start - merge cannon entities
|
||||
+ public final void updateBukkitHandle(Entity entity) {
|
||||
+ if (this.bukkitEntity != null) {
|
||||
+ this.bukkitEntity.setHandle(entity);
|
||||
+ }
|
||||
+ this.bukkitEntity = entity.getBukkitEntity();
|
||||
+ }
|
||||
+
|
||||
+ public final long getPackedOriginPosition() {
|
||||
+ org.bukkit.util.Vector origin = this.getOriginVector();
|
||||
+ if (origin != null) {
|
||||
+ return BlockPos.asLong(origin.getBlockX(), origin.getBlockY(), origin.getBlockZ());
|
||||
+ } else {
|
||||
+ return Long.MIN_VALUE;
|
||||
+ }
|
||||
+ }
|
||||
+ // Sakura end - merge cannon entities
|
||||
|
||||
public Entity(EntityType<?> entityType, Level level) {
|
||||
this.type = entityType;
|
||||
@@ -4964,6 +4981,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
if (this.removalReason != Entity.RemovalReason.UNLOADED_TO_CHUNK) { this.getPassengers().forEach(Entity::stopRiding); } // Paper - rewrite chunk system
|
||||
this.levelCallback.onRemove(removalReason);
|
||||
this.onRemoval(removalReason);
|
||||
+ // Sakura start - merge cannon entities
|
||||
+ if (removalReason == RemovalReason.DISCARDED) {
|
||||
+ this.level.mergeHandler.removeEntity(this);
|
||||
+ }
|
||||
+ // Sakura end - merge cannon entities
|
||||
// Paper start - Folia schedulers
|
||||
if (!(this instanceof ServerPlayer) && removalReason != RemovalReason.CHANGED_DIMENSION && !alreadyRemoved) {
|
||||
// Players need to be special cased, because they are regularly removed from the world
|
||||
diff --git a/net/minecraft/world/entity/item/FallingBlockEntity.java b/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
index 4836be29ff5e87e4f2b9beb0d4a9943281ab5262..1d9afcf995ee734f13803e26956439e5c3450f44 100644
|
||||
--- a/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
+++ b/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
@@ -54,7 +54,7 @@ import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
import org.bukkit.event.entity.EntityRemoveEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
-public class FallingBlockEntity extends Entity {
|
||||
+public class FallingBlockEntity extends Entity implements me.samsuik.sakura.entity.merge.MergeableEntity { // Sakura - merge cannon entities
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
public BlockState blockState = Blocks.SAND.defaultBlockState();
|
||||
public int time;
|
||||
@@ -70,11 +70,62 @@ public class FallingBlockEntity extends Entity {
|
||||
public boolean autoExpire = true; // Paper - Expand FallingBlock API
|
||||
public boolean heightParity; // Sakura - falling block height parity api
|
||||
|
||||
+ // Sakura start - merge cannon entities
|
||||
+ private final me.samsuik.sakura.entity.merge.MergeEntityData mergeData = new me.samsuik.sakura.entity.merge.MergeEntityData(this);
|
||||
+
|
||||
+ @Override
|
||||
+ public final me.samsuik.sakura.entity.merge.MergeEntityData getMergeEntityData() {
|
||||
+ return this.mergeData;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final boolean isSafeToMergeInto(me.samsuik.sakura.entity.merge.MergeableEntity entity, boolean ticksLived) {
|
||||
+ return entity instanceof FallingBlockEntity fbe
|
||||
+ && fbe.blockState.equals(this.blockState)
|
||||
+ && (!ticksLived || fbe.time - 1 == this.time);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final void respawnEntity(int count) {
|
||||
+ while (count-- >= 1) {
|
||||
+ // Unlike PrimedTnt we have to try respawn each stacked entity
|
||||
+ FallingBlockEntity fallingBlock = new FallingBlockEntity(EntityType.FALLING_BLOCK, this.level());
|
||||
+
|
||||
+ // Try to stack the falling block
|
||||
+ this.entityState().apply(fallingBlock);
|
||||
+ fallingBlock.blockState = this.blockState;
|
||||
+ fallingBlock.spawnReason = this.spawnReason;
|
||||
+ fallingBlock.time = this.time - 1;
|
||||
+ fallingBlock.tick();
|
||||
+
|
||||
+ // If you horizontal stack into a moving piston block this condition will be met.
|
||||
+ if (!fallingBlock.isRemoved()) {
|
||||
+ this.mergeData.setCount(count + 1);
|
||||
+ fallingBlock.storeEntityState();
|
||||
+ fallingBlock.entityState().apply(this);
|
||||
+ break;
|
||||
+ } else if (count == 0) {
|
||||
+ this.discard(EntityRemoveEvent.Cause.DESPAWN);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @Nullable ItemEntity spawnAtLocation(ServerLevel level, net.minecraft.world.level.ItemLike item) { // may be overridden by plugins
|
||||
+ ItemEntity itemEntity = null;
|
||||
+ for (int i = 0; i < this.mergeData.getCount(); ++i) {
|
||||
+ itemEntity = super.spawnAtLocation(level, item);
|
||||
+ }
|
||||
+ return itemEntity;
|
||||
+ }
|
||||
+ // Sakura end - merge cannon entities
|
||||
+
|
||||
public FallingBlockEntity(EntityType<? extends FallingBlockEntity> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.heightParity = level.sakuraConfig().cannons.mechanics.fallingBlockParity; // Sakura - configure cannon mechanics
|
||||
this.isFallingBlock = true; // Sakura - client visibility settings
|
||||
this.loadChunks = level.sakuraConfig().cannons.loadChunks; // Sakura - load chunks on movement
|
||||
+ this.mergeData.setMergeLevel(level.sakuraConfig().cannons.mergeLevel); // Sakura - merge cannon entities
|
||||
}
|
||||
|
||||
public FallingBlockEntity(Level level, double x, double y, double z, BlockState state) {
|
||||
@@ -222,6 +273,7 @@ public class FallingBlockEntity extends Entity {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ if (this.respawnEntity()) return; // Sakura - merge cannon entities
|
||||
if (this.level().setBlock(blockPos, this.blockState, 3)) {
|
||||
((ServerLevel)this.level())
|
||||
.getChunkSource()
|
||||
@@ -328,6 +380,7 @@ public class FallingBlockEntity extends Entity {
|
||||
|
||||
compound.putBoolean("CancelDrop", this.cancelDrop);
|
||||
if (!this.autoExpire) compound.putBoolean("Paper.AutoExpire", false); // Paper - Expand FallingBlock API
|
||||
+ compound.putInt("merge_count", this.mergeData.getCount()); // Sakura - merge cannon entities
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -360,6 +413,11 @@ public class FallingBlockEntity extends Entity {
|
||||
this.autoExpire = compound.getBoolean("Paper.AutoExpire");
|
||||
}
|
||||
// Paper end - Expand FallingBlock API
|
||||
+ // Sakura start - merge cannon entities
|
||||
+ if (compound.contains("merge_count", 3)) {
|
||||
+ this.mergeData.setCount(compound.getInt("merge_count"));
|
||||
+ }
|
||||
+ // Sakura end - merge cannon entities
|
||||
}
|
||||
|
||||
public void setHurtsEntities(float fallDamagePerDistance, int fallDamageMax) {
|
||||
diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
index 9e9463d62aa1618a4a749bb7e2636c9b090991e9..b378b4c4930c4ebd55795591aca173fd1fee46c9 100644
|
||||
--- a/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
+++ b/net/minecraft/world/entity/item/PrimedTnt.java
|
||||
@@ -33,7 +33,7 @@ import org.bukkit.event.entity.EntityRemoveEvent;
|
||||
import org.bukkit.event.entity.ExplosionPrimeEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
-public class PrimedTnt extends Entity implements TraceableEntity {
|
||||
+public class PrimedTnt extends Entity implements TraceableEntity, me.samsuik.sakura.entity.merge.MergeableEntity { // Sakura - merge cannon entities
|
||||
private static final EntityDataAccessor<Integer> DATA_FUSE_ID = SynchedEntityData.defineId(PrimedTnt.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<BlockState> DATA_BLOCK_STATE_ID = SynchedEntityData.defineId(PrimedTnt.class, EntityDataSerializers.BLOCK_STATE);
|
||||
private static final int DEFAULT_FUSE_TIME = 80;
|
||||
@@ -58,11 +58,48 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
||||
public float explosionPower = 4.0F;
|
||||
public boolean isIncendiary = false; // CraftBukkit - add field
|
||||
|
||||
+ // Sakura start - merge cannon entities
|
||||
+ private final me.samsuik.sakura.entity.merge.MergeEntityData mergeData = new me.samsuik.sakura.entity.merge.MergeEntityData(this);
|
||||
+
|
||||
+ @Override
|
||||
+ public final me.samsuik.sakura.entity.merge.MergeEntityData getMergeEntityData() {
|
||||
+ return this.mergeData;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final boolean isSafeToMergeInto(me.samsuik.sakura.entity.merge.MergeableEntity entity, boolean ticksLived) {
|
||||
+ return entity instanceof PrimedTnt tnt
|
||||
+ && tnt.getFuse() + 1 == this.getFuse()
|
||||
+ // required to prevent issues with powdered snow
|
||||
+ && (tnt.entityState().fallDistance() == this.fallDistance
|
||||
+ || tnt.entityState().fallDistance() > 2.5f && this.fallDistance > 2.5f);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public final void respawnEntity(int count) {
|
||||
+ PrimedTnt tnt = new PrimedTnt(EntityType.TNT, this.level());
|
||||
+ tnt.updateBukkitHandle(this); // update handle for plugins
|
||||
+ while (count-- > 1) {
|
||||
+ this.setFuse(100); // Prevent unwanted explosions while ticking
|
||||
+
|
||||
+ // Cause an explosion to affect this entity
|
||||
+ tnt.setPos(this.position());
|
||||
+ tnt.setDeltaMovement(this.getDeltaMovement());
|
||||
+ this.entityState().apply(this);
|
||||
+ tnt.explode();
|
||||
+ this.storeEntityState();
|
||||
+
|
||||
+ this.tick();
|
||||
+ }
|
||||
+ }
|
||||
+ // Sakura end - merge cannon entities
|
||||
+
|
||||
public PrimedTnt(EntityType<? extends PrimedTnt> entityType, Level level) {
|
||||
super(entityType, level);
|
||||
this.blocksBuilding = true;
|
||||
this.isPrimedTNT = true; // Sakura - client visibility settings
|
||||
this.loadChunks = level.sakuraConfig().cannons.loadChunks; // Sakura - load chunks on movement
|
||||
+ this.mergeData.setMergeLevel(level.sakuraConfig().cannons.mergeLevel); // Sakura - merge cannon entities
|
||||
}
|
||||
|
||||
public PrimedTnt(Level level, double x, double y, double z, @Nullable LivingEntity owner) {
|
||||
@@ -142,6 +179,7 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
||||
if (i <= 0) {
|
||||
// CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event
|
||||
//this.discard();
|
||||
+ this.respawnEntity(); // Sakura - merge cannon entities
|
||||
if (!this.level().isClientSide) {
|
||||
this.explode();
|
||||
}
|
||||
@@ -212,6 +250,7 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
||||
if (this.explosionPower != 4.0F) {
|
||||
compound.putFloat("explosion_power", this.explosionPower);
|
||||
}
|
||||
+ compound.putInt("merge_count", this.mergeData.getCount()); // Sakura - merge cannon entities
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -224,6 +263,11 @@ public class PrimedTnt extends Entity implements TraceableEntity {
|
||||
if (compound.contains("explosion_power", 99)) {
|
||||
this.explosionPower = Mth.clamp(compound.getFloat("explosion_power"), 0.0F, 128.0F);
|
||||
}
|
||||
+ // Sakura start - merge cannon entities
|
||||
+ if (compound.contains("merge_count", 3)) {
|
||||
+ this.mergeData.setCount(compound.getInt("merge_count"));
|
||||
+ }
|
||||
+ // Sakura end - merge cannon entities
|
||||
}
|
||||
|
||||
@Nullable
|
||||
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
|
||||
index e4584cd58d84f2b64748dfec7a1aa69fca119021..a4d7c95514c3db5799efe178efee796413d1bac8 100644
|
||||
--- a/net/minecraft/world/level/Level.java
|
||||
+++ b/net/minecraft/world/level/Level.java
|
||||
@@ -837,6 +837,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl
|
||||
return chunk != null ? chunk.getNoiseBiome(x, y, z) : this.getUncachedNoiseBiome(x, y, z);
|
||||
}
|
||||
// Paper end - optimise random ticking
|
||||
+ public final me.samsuik.sakura.entity.merge.EntityMergeHandler mergeHandler = new me.samsuik.sakura.entity.merge.EntityMergeHandler(); // Sakura - merge cannon entities
|
||||
|
||||
protected Level(
|
||||
WritableLevelData levelData,
|
||||
diff --git a/net/minecraft/world/level/block/BasePressurePlateBlock.java b/net/minecraft/world/level/block/BasePressurePlateBlock.java
|
||||
index 108c1d23bf80777b943edfa0b5585ebb928540a7..69d490c79e30fb42da69bbd804ecaea7b88fe7b0 100644
|
||||
--- a/net/minecraft/world/level/block/BasePressurePlateBlock.java
|
||||
+++ b/net/minecraft/world/level/block/BasePressurePlateBlock.java
|
||||
@@ -91,7 +91,7 @@ public abstract class BasePressurePlateBlock extends Block {
|
||||
}
|
||||
|
||||
private void checkPressed(@Nullable Entity entity, Level level, BlockPos pos, BlockState state, int currentSignal) {
|
||||
- int signalStrength = this.getSignalStrength(level, pos);
|
||||
+ int signalStrength = this.getSignalStrength(level, pos, currentSignal == 0); // Sakura - merge cannon entities
|
||||
boolean flag = currentSignal > 0;
|
||||
boolean flag1 = signalStrength > 0;
|
||||
|
||||
@@ -168,6 +168,12 @@ public abstract class BasePressurePlateBlock extends Block {
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
+ // Sakura start - merge cannon entities
|
||||
+ protected int getSignalStrength(Level world, BlockPos pos, boolean entityInside) {
|
||||
+ return this.getSignalStrength(world, pos);
|
||||
+ }
|
||||
+ // Sakura end - merge cannon entities
|
||||
+
|
||||
protected abstract int getSignalStrength(Level level, BlockPos pos);
|
||||
|
||||
protected abstract int getSignalForState(BlockState state);
|
||||
diff --git a/net/minecraft/world/level/block/WeightedPressurePlateBlock.java b/net/minecraft/world/level/block/WeightedPressurePlateBlock.java
|
||||
index 0ad494a861c04aeacb0620000e306cfab813fdde..c49044097fa8d3294de10a681717cd424e6c1078 100644
|
||||
--- a/net/minecraft/world/level/block/WeightedPressurePlateBlock.java
|
||||
+++ b/net/minecraft/world/level/block/WeightedPressurePlateBlock.java
|
||||
@@ -40,6 +40,11 @@ public class WeightedPressurePlateBlock extends BasePressurePlateBlock {
|
||||
|
||||
@Override
|
||||
protected int getSignalStrength(Level level, BlockPos pos) {
|
||||
+ // Sakura start - merge cannon entities
|
||||
+ return this.getSignalStrength(level, pos, false);
|
||||
+ }
|
||||
+ protected final int getSignalStrength(Level level, BlockPos pos, boolean entityInside) {
|
||||
+ // Sakura end - merge cannon entities
|
||||
// CraftBukkit start
|
||||
// int min = Math.min(getEntityCount(level, TOUCH_AABB.move(pos), Entity.class), this.maxWeight);
|
||||
int min = 0;
|
||||
@@ -55,7 +60,7 @@ public class WeightedPressurePlateBlock extends BasePressurePlateBlock {
|
||||
|
||||
// We only want to block turning the plate on if all events are cancelled
|
||||
if (!cancellable.isCancelled()) {
|
||||
- min++;
|
||||
+ min += !entityInside && entity instanceof me.samsuik.sakura.entity.merge.MergeableEntity mergeEntity ? mergeEntity.getMergeEntityData().getCount() : 1; // Sakura - merge cannon entities
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samsuik <kfian294ma4@gmail.com>
|
||||
Date: Tue, 21 Sep 2021 23:54:25 +0100
|
||||
Subject: [PATCH] Client Visibility Settings
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
index dab4d38a2408af5f8f415e5a916845bf08ec536c..7f8bbddc7b5b0c8d13a2772b91df44d6e99aadf3 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
@@ -2385,6 +2385,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
handle.keepLevel = data.getBoolean("keepLevel");
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Sakura start - client visibility settings; load from nbt
|
||||
+ if (nbttagcompound.contains("sakura", 10)) {
|
||||
+ CompoundTag sakuraTag = nbttagcompound.getCompound("sakura");
|
||||
+ this.getHandle().visibilitySettings.loadData(sakuraTag);
|
||||
+ }
|
||||
+ // Sakura end - client visibility settings; load from nbt
|
||||
}
|
||||
|
||||
public void setExtraData(CompoundTag nbttagcompound) {
|
||||
@@ -2414,6 +2421,11 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
paper.putLong("LastLogin", handle.loginTime);
|
||||
paper.putLong("LastSeen", System.currentTimeMillis());
|
||||
// Paper end
|
||||
+ // Sakura start - client visibility settings; save to nbt
|
||||
+ CompoundTag sakuraTag = nbttagcompound.getCompound("sakura");
|
||||
+ this.getHandle().visibilitySettings.saveData(sakuraTag);
|
||||
+ nbttagcompound.put("sakura", sakuraTag);
|
||||
+ // Sakura end - client visibility settings; save to nbt
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3085,6 +3097,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
return this.getHandle().allowsListing();
|
||||
}
|
||||
|
||||
+ // Sakura start - client visibility settings; api
|
||||
+ @Override
|
||||
+ public final me.samsuik.sakura.player.visibility.VisibilitySettings getVisibility() {
|
||||
+ return this.getHandle().visibilitySettings;
|
||||
+ }
|
||||
+ // Sakura end - client visibility settings; api
|
||||
+
|
||||
// Paper start
|
||||
@Override
|
||||
public net.kyori.adventure.text.Component displayName() {
|
||||
@@ -0,0 +1,72 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Samsuik <kfian294ma4@gmail.com>
|
||||
Date: Sat, 9 Sep 2023 18:39:15 +0100
|
||||
Subject: [PATCH] Merge Cannon Entities
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java
|
||||
index 22b6016a8d6828b2b10c028b24fd160b3b9f9f59..ad924326b6bfd01b719096ff53ed4b8e513e25af 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftFallingBlock.java
|
||||
@@ -26,6 +26,28 @@ public class CraftFallingBlock extends CraftEntity implements FallingBlock {
|
||||
}
|
||||
// Sakura end - falling block height parity api
|
||||
|
||||
+ // Sakura start - merge cannon entities
|
||||
+ @Override
|
||||
+ public @org.jetbrains.annotations.NotNull me.samsuik.sakura.entity.merge.MergeLevel getMergeLevel() {
|
||||
+ return this.getHandle().getMergeEntityData().getMergeLevel();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setMergeLevel(@org.jetbrains.annotations.NotNull me.samsuik.sakura.entity.merge.MergeLevel level) {
|
||||
+ this.getHandle().getMergeEntityData().setMergeLevel(level);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int getStacked() {
|
||||
+ return this.getHandle().getMergeEntityData().getCount();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setStacked(int stacked) {
|
||||
+ this.getHandle().getMergeEntityData().setCount(stacked);
|
||||
+ }
|
||||
+ // Sakura end - merge cannon entities
|
||||
+
|
||||
@Override
|
||||
public FallingBlockEntity getHandle() {
|
||||
return (FallingBlockEntity) this.entity;
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java
|
||||
index a61aec087fa7cec27a803668bdc1b9e6eb336755..c6f36ab2368d0e2e4555d5f8edc0132dcb61a53c 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTNTPrimed.java
|
||||
@@ -12,6 +12,28 @@ public class CraftTNTPrimed extends CraftEntity implements TNTPrimed {
|
||||
super(server, entity);
|
||||
}
|
||||
|
||||
+ // Sakura start - merge cannon entities
|
||||
+ @Override
|
||||
+ public @org.jetbrains.annotations.NotNull me.samsuik.sakura.entity.merge.MergeLevel getMergeLevel() {
|
||||
+ return this.getHandle().getMergeEntityData().getMergeLevel();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setMergeLevel(@org.jetbrains.annotations.NotNull me.samsuik.sakura.entity.merge.MergeLevel level) {
|
||||
+ this.getHandle().getMergeEntityData().setMergeLevel(level);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int getStacked() {
|
||||
+ return this.getHandle().getMergeEntityData().getCount();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setStacked(int stacked) {
|
||||
+ this.getHandle().getMergeEntityData().setCount(stacked);
|
||||
+ }
|
||||
+ // Sakura end - merge cannon entities
|
||||
+
|
||||
@Override
|
||||
public float getYield() {
|
||||
return this.getHandle().explosionPower;
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
||||
@@ -115,6 +_,18 @@
|
||||
@@ -116,6 +_,18 @@
|
||||
throw new AssertionError("Unknown entity " + (entity == null ? null : entity.getClass()));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ package me.samsuik.sakura.command;
|
||||
|
||||
import me.samsuik.sakura.command.subcommands.ConfigCommand;
|
||||
import me.samsuik.sakura.command.subcommands.TPSCommand;
|
||||
import me.samsuik.sakura.command.subcommands.FPSCommand;
|
||||
import me.samsuik.sakura.command.subcommands.VisualCommand;
|
||||
import me.samsuik.sakura.player.visibility.VisibilityTypes;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.bukkit.command.Command;
|
||||
|
||||
@@ -14,6 +17,9 @@ public final class SakuraCommands {
|
||||
COMMANDS.put("sakura", new SakuraCommand("sakura"));
|
||||
COMMANDS.put("config", new ConfigCommand("config"));
|
||||
COMMANDS.put("tps", new TPSCommand("tps"));
|
||||
COMMANDS.put("fps", new FPSCommand("fps"));
|
||||
COMMANDS.put("tntvisibility", new VisualCommand(VisibilityTypes.TNT, "tnttoggle"));
|
||||
COMMANDS.put("sandvisibility", new VisualCommand(VisibilityTypes.SAND, "sandtoggle"));
|
||||
}
|
||||
|
||||
public static void registerCommands(MinecraftServer server) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package me.samsuik.sakura.command.subcommands;
|
||||
|
||||
import me.samsuik.sakura.command.BaseSubCommand;
|
||||
import me.samsuik.sakura.player.visibility.VisibilityGui;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class FPSCommand extends BaseSubCommand {
|
||||
private final VisibilityGui visibilityGui = new VisibilityGui();
|
||||
|
||||
public FPSCommand(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if (sender instanceof Player player) {
|
||||
this.visibilityGui.showTo(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package me.samsuik.sakura.command.subcommands;
|
||||
|
||||
import me.samsuik.sakura.command.BaseSubCommand;
|
||||
import me.samsuik.sakura.configuration.GlobalConfiguration;
|
||||
import me.samsuik.sakura.player.visibility.VisibilitySettings;
|
||||
import me.samsuik.sakura.player.visibility.VisibilityState;
|
||||
import me.samsuik.sakura.player.visibility.VisibilityType;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class VisualCommand extends BaseSubCommand {
|
||||
private final VisibilityType type;
|
||||
|
||||
public VisualCommand(VisibilityType type, String... aliases) {
|
||||
super(type.key() + "visibility");
|
||||
this.setAliases(Arrays.asList(aliases));
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
VisibilitySettings settings = player.getVisibility();
|
||||
VisibilityState state = settings.toggle(type);
|
||||
|
||||
String stateName = (state == VisibilityState.ON) ? "Enabled" : "Disabled";
|
||||
player.sendRichMessage(GlobalConfiguration.get().messages.fpsSettingChange,
|
||||
Placeholder.unparsed("name", this.type.key()),
|
||||
Placeholder.unparsed("state", stateName)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package me.samsuik.sakura.entity;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@NullMarked
|
||||
public record EntityState(Vec3 position, Vec3 momentum, AABB bb, Vec3 stuckSpeed, Optional<BlockPos> supportingPos, boolean onGround, float fallDistance) {
|
||||
public static EntityState of(Entity entity) {
|
||||
return new EntityState(
|
||||
entity.position(), entity.getDeltaMovement(), entity.getBoundingBox(),
|
||||
entity.stuckSpeedMultiplier(), entity.mainSupportingBlockPos,
|
||||
entity.onGround(), entity.fallDistance
|
||||
);
|
||||
}
|
||||
|
||||
public void apply(Entity entity) {
|
||||
entity.setPos(this.position);
|
||||
entity.setDeltaMovement(this.momentum);
|
||||
entity.setBoundingBox(this.bb);
|
||||
entity.makeStuckInBlock(Blocks.AIR.defaultBlockState(), this.stuckSpeed);
|
||||
entity.onGround = this.onGround;
|
||||
entity.mainSupportingBlockPos = this.supportingPos;
|
||||
entity.fallDistance = this.fallDistance;
|
||||
}
|
||||
|
||||
public void applyEntityPosition(Entity entity) {
|
||||
entity.setPos(this.position);
|
||||
entity.setBoundingBox(this.bb);
|
||||
}
|
||||
|
||||
public boolean comparePositionAndMotion(Entity entity) {
|
||||
return entity.position().equals(this.position)
|
||||
&& entity.getDeltaMovement().equals(this.momentum);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package me.samsuik.sakura.entity.merge;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public final class EntityMergeHandler {
|
||||
private final TrackedMergeHistory trackedHistory = new TrackedMergeHistory();
|
||||
|
||||
/**
|
||||
* Tries to merge the provided entities using the {@link MergeStrategy}.
|
||||
*
|
||||
* @param previous the last entity to tick
|
||||
* @param entity the entity being merged
|
||||
* @return success
|
||||
*/
|
||||
public boolean tryMerge(@Nullable Entity entity, @Nullable Entity previous) {
|
||||
if (entity instanceof MergeableEntity mergeEntity && previous instanceof MergeableEntity) {
|
||||
MergeEntityData mergeEntityData = mergeEntity.getMergeEntityData();
|
||||
MergeStrategy strategy = MergeStrategy.from(mergeEntityData.getMergeLevel());
|
||||
Entity into = strategy.mergeEntity(entity, previous, this.trackedHistory);
|
||||
if (into instanceof MergeableEntity intoEntity && !into.isRemoved() && mergeEntity.isSafeToMergeInto(intoEntity, strategy.trackHistory())) {
|
||||
return this.mergeEntity(mergeEntity, intoEntity);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the merged data of the provided entities if the {@link MergeStrategy} requires it.
|
||||
*
|
||||
* @param entity provided entity
|
||||
*/
|
||||
public void removeEntity(@Nullable Entity entity) {
|
||||
if (entity instanceof MergeableEntity mergeEntity) {
|
||||
MergeEntityData mergeEntityData = mergeEntity.getMergeEntityData();
|
||||
MergeStrategy strategy = MergeStrategy.from(mergeEntityData.getMergeLevel());
|
||||
if (mergeEntityData.hasMerged() && strategy.trackHistory()) {
|
||||
this.trackedHistory.trackHistory(entity, mergeEntityData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called every tick and provided the current server tick to remove any unneeded merge history.
|
||||
*
|
||||
* @param tick server tick
|
||||
*/
|
||||
public void expire(int tick) {
|
||||
if (tick % 200 == 0) {
|
||||
this.trackedHistory.expire(tick);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the first entity into the second. The entity merge count can be retrieved through the {@link MergeEntityData}.
|
||||
* <p>
|
||||
* This method also updates the bukkit handle so that plugins reference the first entity after the second entity has been removed.
|
||||
*
|
||||
* @param mergeEntity the first entity
|
||||
* @param into the entity to merge into
|
||||
* @return if successful
|
||||
*/
|
||||
public boolean mergeEntity(@NotNull MergeableEntity mergeEntity, @NotNull MergeableEntity into) {
|
||||
MergeEntityData entities = mergeEntity.getMergeEntityData();
|
||||
MergeEntityData mergeInto = into.getMergeEntityData();
|
||||
mergeInto.mergeWith(entities); // merge entities together
|
||||
|
||||
// discard the entity and update the bukkit handle
|
||||
Entity nmsEntity = (Entity) mergeEntity;
|
||||
nmsEntity.discard();
|
||||
nmsEntity.updateBukkitHandle((Entity) into);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package me.samsuik.sakura.entity.merge;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface MergeCondition {
|
||||
default MergeCondition and(@NotNull MergeCondition condition) {
|
||||
return (e,c,t) -> this.accept(e,c,t) && condition.accept(e,c,t);
|
||||
}
|
||||
|
||||
default MergeCondition or(@NotNull MergeCondition condition) {
|
||||
return (e,c,t) -> this.accept(e,c,t) || condition.accept(e,c,t);
|
||||
}
|
||||
|
||||
boolean accept(@NotNull Entity entity, int attempts, long sinceCreation);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package me.samsuik.sakura.entity.merge;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class MergeEntityData {
|
||||
private final Entity entity;
|
||||
private List<MergeEntityData> connected = new ObjectArrayList<>();
|
||||
private int count = 1;
|
||||
private MergeLevel mergeLevel = MergeLevel.NONE;
|
||||
|
||||
public MergeEntityData(Entity entity) {
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
public void mergeWith(@NotNull MergeEntityData mergeEntityData) {
|
||||
this.connected.add(mergeEntityData);
|
||||
this.connected.addAll(mergeEntityData.connected);
|
||||
this.count += mergeEntityData.getCount();
|
||||
mergeEntityData.setCount(0);
|
||||
}
|
||||
|
||||
public LongOpenHashSet getOriginPositions() {
|
||||
LongOpenHashSet positions = new LongOpenHashSet();
|
||||
this.connected.forEach(entityData -> positions.add(entityData.entity.getPackedOriginPosition()));
|
||||
return positions;
|
||||
}
|
||||
|
||||
public boolean hasMerged() {
|
||||
return !this.connected.isEmpty() && this.count != 0;
|
||||
}
|
||||
|
||||
public void setMergeLevel(MergeLevel mergeLevel) {
|
||||
this.mergeLevel = mergeLevel;
|
||||
}
|
||||
|
||||
public MergeLevel getMergeLevel() {
|
||||
return mergeLevel;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return this.count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package me.samsuik.sakura.entity.merge;
|
||||
|
||||
import me.samsuik.sakura.utils.collections.FixedSizeCustomObjectTable;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.entity.EntityTickList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface MergeStrategy {
|
||||
/**
|
||||
* If this merge strategy requires the merge history to be tracked.
|
||||
*
|
||||
* @return should track history
|
||||
*/
|
||||
boolean trackHistory();
|
||||
|
||||
/**
|
||||
* Tries to merge the first entity into another entity.
|
||||
* <p>
|
||||
* The first entity should always be positioned right after the second entity in the
|
||||
* {@link EntityTickList}. This method should only
|
||||
* be called before the first entity and after the second entity has ticked.
|
||||
*
|
||||
* @param entity current entity
|
||||
* @param previous last entity to tick
|
||||
* @return success
|
||||
*/
|
||||
Entity mergeEntity(@NotNull Entity entity, @NotNull Entity previous, @NotNull TrackedMergeHistory mergeHistory);
|
||||
|
||||
/**
|
||||
* Gets the {@link MergeStrategy} for the {@link MergeLevel}.
|
||||
*
|
||||
* @param level provided level
|
||||
* @return strategy
|
||||
*/
|
||||
static MergeStrategy from(MergeLevel level) {
|
||||
return switch (level) {
|
||||
case NONE -> None.INSTANCE;
|
||||
case STRICT -> Strict.INSTANCE;
|
||||
case LENIENT -> Lenient.INSTANCE;
|
||||
case SPAWN -> Spawn.INSTANCE;
|
||||
};
|
||||
}
|
||||
|
||||
final class None implements MergeStrategy {
|
||||
private static final None INSTANCE = new None();
|
||||
|
||||
@Override
|
||||
public boolean trackHistory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity mergeEntity(@NotNull Entity entity, @NotNull Entity previous, @NotNull TrackedMergeHistory mergeHistory) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
final class Strict implements MergeStrategy {
|
||||
private static final Strict INSTANCE = new Strict();
|
||||
|
||||
@Override
|
||||
public boolean trackHistory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity mergeEntity(@NotNull Entity entity, @NotNull Entity previous, @NotNull TrackedMergeHistory mergeHistory) {
|
||||
return entity.compareState(previous) ? previous : null;
|
||||
}
|
||||
}
|
||||
|
||||
final class Lenient implements MergeStrategy {
|
||||
private static final Lenient INSTANCE = new Lenient();
|
||||
private final FixedSizeCustomObjectTable<Entity> entityTable = new FixedSizeCustomObjectTable<>(512, entity -> {
|
||||
return entity.blockPosition().hashCode();
|
||||
});
|
||||
|
||||
@Override
|
||||
public boolean trackHistory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity mergeEntity(@NotNull Entity entity, @NotNull Entity previous, @NotNull TrackedMergeHistory mergeHistory) {
|
||||
if (entity.compareState(previous)) {
|
||||
return previous;
|
||||
}
|
||||
|
||||
Entity nextEntity = this.entityTable.getAndWrite(entity);
|
||||
if (nextEntity == null || !nextEntity.level().equals(entity.level())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mergeHistory.hasPreviousMerged(entity, nextEntity) && entity.compareState(nextEntity) ? nextEntity : null;
|
||||
}
|
||||
}
|
||||
|
||||
final class Spawn implements MergeStrategy {
|
||||
private static final Spawn INSTANCE = new Spawn();
|
||||
private static final MergeCondition CONDITION = (e, shots, time) -> (shots > 16 || time >= 200);
|
||||
|
||||
@Override
|
||||
public boolean trackHistory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity mergeEntity(@NotNull Entity entity, @NotNull Entity previous, @NotNull TrackedMergeHistory mergeHistory) {
|
||||
final Entity mergeInto;
|
||||
if (entity.tickCount == 1 && mergeHistory.hasPreviousMerged(entity, previous) && mergeHistory.hasMetCondition(previous, CONDITION)) {
|
||||
mergeInto = previous;
|
||||
} else {
|
||||
mergeInto = entity.compareState(previous) ? previous : null;
|
||||
}
|
||||
return mergeInto;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.samsuik.sakura.entity.merge;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface MergeableEntity {
|
||||
@NotNull MergeEntityData getMergeEntityData();
|
||||
|
||||
boolean isSafeToMergeInto(@NotNull MergeableEntity entity, boolean ticksLived);
|
||||
|
||||
default boolean respawnEntity() {
|
||||
MergeEntityData mergeData = this.getMergeEntityData();
|
||||
int count = mergeData.getCount();
|
||||
if (count > 1) {
|
||||
mergeData.setCount(0);
|
||||
this.respawnEntity(count);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void respawnEntity(int count);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package me.samsuik.sakura.entity.merge;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import me.samsuik.sakura.configuration.WorldConfiguration.Cannons.Mechanics.TNTSpread;
|
||||
import me.samsuik.sakura.utils.TickExpiry;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.item.FallingBlockEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class TrackedMergeHistory {
|
||||
private final Long2ObjectMap<PositionHistory> historyMap = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
public boolean hasPreviousMerged(@NotNull Entity entity, @NotNull Entity into) {
|
||||
PositionHistory positions = this.getHistory(into);
|
||||
return positions != null && positions.has(entity.getPackedOriginPosition());
|
||||
}
|
||||
|
||||
public void trackHistory(@NotNull Entity entity, @NotNull MergeEntityData mergeEntityData) {
|
||||
long originPosition = entity.getPackedOriginPosition();
|
||||
PositionHistory positions = this.historyMap.computeIfAbsent(originPosition, p -> new PositionHistory());
|
||||
LongOpenHashSet originPositions = mergeEntityData.getOriginPositions();
|
||||
boolean createHistory = positions.hasTimePassed(160);
|
||||
if (createHistory && (entity instanceof FallingBlockEntity || entity.level().sakuraConfig().cannons.mechanics.tntSpread == TNTSpread.ALL)) {
|
||||
originPositions.forEach(pos -> this.historyMap.put(pos, positions));
|
||||
}
|
||||
positions.trackPositions(originPositions, !createHistory);
|
||||
}
|
||||
|
||||
public boolean hasMetCondition(@NotNull Entity entity, MergeCondition condition) {
|
||||
PositionHistory positions = this.getHistory(entity);
|
||||
return positions != null && positions.hasMetConditions(entity, condition);
|
||||
}
|
||||
|
||||
private PositionHistory getHistory(Entity entity) {
|
||||
long originPosition = entity.getPackedOriginPosition();
|
||||
return this.historyMap.get(originPosition);
|
||||
}
|
||||
|
||||
public void expire(int tick) {
|
||||
this.historyMap.values().removeIf(p -> p.expiry().isExpired(tick));
|
||||
}
|
||||
|
||||
private static final class PositionHistory {
|
||||
private final LongOpenHashSet positions = new LongOpenHashSet();
|
||||
private final TickExpiry expiry = new TickExpiry(MinecraftServer.currentTick, 200);
|
||||
private final long created = MinecraftServer.currentTick;
|
||||
private int cycles = 0;
|
||||
|
||||
public TickExpiry expiry() {
|
||||
return this.expiry;
|
||||
}
|
||||
|
||||
public boolean has(long position) {
|
||||
this.expiry.refresh(MinecraftServer.currentTick);
|
||||
return this.positions.contains(position);
|
||||
}
|
||||
|
||||
public void trackPositions(LongOpenHashSet positions, boolean retain) {
|
||||
if (retain) {
|
||||
this.positions.retainAll(positions);
|
||||
} else {
|
||||
this.positions.addAll(positions);
|
||||
}
|
||||
this.cycles++;
|
||||
}
|
||||
|
||||
public boolean hasMetConditions(@NotNull Entity entity, @NotNull MergeCondition condition) {
|
||||
this.expiry.refresh(MinecraftServer.currentTick);
|
||||
return condition.accept(entity, this.cycles, this.timeSinceCreation());
|
||||
}
|
||||
|
||||
public boolean hasTimePassed(int ticks) {
|
||||
return this.timeSinceCreation() > ticks;
|
||||
}
|
||||
|
||||
private long timeSinceCreation() {
|
||||
return MinecraftServer.currentTick - this.created;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import java.util.function.LongConsumer;
|
||||
public final class LocalConfigManager implements LocalStorageHandler {
|
||||
private static final int SMALL_REGION_SIZE = 12;
|
||||
private static final int CONFIG_CACHE_EXPIRATION = 600;
|
||||
|
||||
|
||||
private final Map<LocalRegion, LocalValueStorage> storageMap = new Object2ObjectOpenHashMap<>();
|
||||
private final List<LocalRegion> largeRegions = new ObjectArrayList<>();
|
||||
private final Long2ObjectMap<List<LocalRegion>> smallRegions = new Long2ObjectOpenHashMap<>();
|
||||
@@ -41,7 +41,7 @@ public final class LocalConfigManager implements LocalStorageHandler {
|
||||
int regionX = x >> this.regionExponent;
|
||||
int regionZ = z >> this.regionExponent;
|
||||
long regionPos = ChunkPos.asLong(regionX, regionZ);
|
||||
List<LocalRegion> regions = this.smallRegions.get(regionPos);
|
||||
List<LocalRegion> regions = this.smallRegions.getOrDefault(regionPos, List.of());
|
||||
for (LocalRegion region : Iterables.concat(regions, this.largeRegions)) {
|
||||
if (region.contains(x, z)) {
|
||||
return Optional.of(region);
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package me.samsuik.sakura.player.gui;
|
||||
|
||||
import me.samsuik.sakura.player.gui.components.GuiComponent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public abstract class FeatureGui {
|
||||
private final int size;
|
||||
private final Component title;
|
||||
|
||||
public FeatureGui(int size, Component title) {
|
||||
this.size = size;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
protected abstract void fillInventory(Inventory inventory);
|
||||
|
||||
protected abstract void afterFill(Player player, FeatureGuiInventory inventory);
|
||||
|
||||
public final void showTo(Player bukkitPlayer) {
|
||||
FeatureGuiInventory featureInventory = new FeatureGuiInventory(this, this.size, this.title);
|
||||
this.fillInventory(featureInventory.getInventory());
|
||||
this.afterFill(bukkitPlayer, featureInventory);
|
||||
bukkitPlayer.openInventory(featureInventory.getInventory());
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void clickEvent(InventoryClickEvent event) {
|
||||
Inventory clicked = event.getClickedInventory();
|
||||
if (clicked != null && clicked.getHolder(false) instanceof FeatureGuiInventory featureInventory) {
|
||||
event.setCancelled(true);
|
||||
for (GuiComponent component : featureInventory.getComponents().reversed()) {
|
||||
if (component.interaction(event, featureInventory)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package me.samsuik.sakura.player.gui;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Multimap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import me.samsuik.sakura.player.gui.components.GuiComponent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
||||
@NullMarked
|
||||
public final class FeatureGuiInventory implements InventoryHolder {
|
||||
private final Inventory inventory;
|
||||
private final FeatureGui gui;
|
||||
private final Multimap<NamespacedKey, GuiComponent> componentsUnderKey = HashMultimap.create();
|
||||
private final Object2ObjectMap<GuiComponent, NamespacedKey> componentKeys = new Object2ObjectLinkedOpenHashMap<>();
|
||||
|
||||
public FeatureGuiInventory(FeatureGui gui, int size, Component component) {
|
||||
this.inventory = Bukkit.createInventory(this, size, component);
|
||||
this.gui = gui;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Inventory getInventory() {
|
||||
return this.inventory;
|
||||
}
|
||||
|
||||
public FeatureGui getGui() {
|
||||
return this.gui;
|
||||
}
|
||||
|
||||
public ImmutableList<GuiComponent> getComponents() {
|
||||
return ImmutableList.copyOf(this.componentKeys.keySet());
|
||||
}
|
||||
|
||||
public ImmutableList<GuiComponent> findComponents(NamespacedKey key) {
|
||||
return ImmutableList.copyOf(this.componentsUnderKey.get(key));
|
||||
}
|
||||
|
||||
public Optional<GuiComponent> findFirst(NamespacedKey key) {
|
||||
Collection<GuiComponent> components = this.componentsUnderKey.get(key);
|
||||
return components.stream().findFirst();
|
||||
}
|
||||
|
||||
public void removeComponents(NamespacedKey key) {
|
||||
Collection<GuiComponent> removed = this.componentsUnderKey.removeAll(key);
|
||||
for (GuiComponent component : removed) {
|
||||
this.componentKeys.remove(component);
|
||||
}
|
||||
}
|
||||
|
||||
public void addComponent(GuiComponent component, NamespacedKey key) {
|
||||
Preconditions.checkArgument(!this.componentKeys.containsKey(component), "component has already been added");
|
||||
this.componentKeys.put(component, key);
|
||||
this.componentsUnderKey.put(key, component);
|
||||
this.inventoryUpdate(component);
|
||||
}
|
||||
|
||||
public void removeComponent(GuiComponent component) {
|
||||
NamespacedKey key = this.componentKeys.remove(component);
|
||||
this.componentsUnderKey.remove(key, component);
|
||||
}
|
||||
|
||||
public void replaceComponent(GuiComponent component, GuiComponent replacement) {
|
||||
NamespacedKey key = this.componentKeys.remove(component);
|
||||
Preconditions.checkNotNull(key, "component does not exist");
|
||||
this.componentKeys.put(replacement, key);
|
||||
this.componentsUnderKey.remove(key, component);
|
||||
this.componentsUnderKey.put(key, replacement);
|
||||
this.inventoryUpdate(replacement);
|
||||
}
|
||||
|
||||
public void removeAllComponents() {
|
||||
this.componentKeys.clear();
|
||||
this.componentsUnderKey.clear();
|
||||
}
|
||||
|
||||
private void inventoryUpdate(GuiComponent component) {
|
||||
component.creation(this.inventory);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package me.samsuik.sakura.player.gui;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public class ItemStackUtil {
|
||||
public static ItemStack itemWithBlankName(Material material) {
|
||||
return itemWithName(material, Component.empty());
|
||||
}
|
||||
|
||||
public static ItemStack itemWithName(Material material, Component component) {
|
||||
ItemStack item = new ItemStack(material);
|
||||
item.editMeta(m -> m.itemName(component));
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package me.samsuik.sakura.player.gui.components;
|
||||
|
||||
import me.samsuik.sakura.player.gui.FeatureGuiInventory;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface GuiClickEvent {
|
||||
void doSomething(InventoryClickEvent event, FeatureGuiInventory inventory);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package me.samsuik.sakura.player.gui.components;
|
||||
|
||||
import me.samsuik.sakura.player.gui.FeatureGuiInventory;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public interface GuiComponent {
|
||||
boolean interaction(InventoryClickEvent event, FeatureGuiInventory featureInventory);
|
||||
|
||||
void creation(Inventory inventory);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package me.samsuik.sakura.player.gui.components;
|
||||
|
||||
import me.samsuik.sakura.player.gui.FeatureGuiInventory;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
@NullMarked
|
||||
public final class ItemButton implements GuiComponent {
|
||||
private final ItemStack bukkitItem;
|
||||
private final int slot;
|
||||
private final GuiClickEvent whenClicked;
|
||||
|
||||
public ItemButton(ItemStack bukkitItem, int slot, GuiClickEvent whenClicked) {
|
||||
this.bukkitItem = bukkitItem;
|
||||
this.slot = slot;
|
||||
this.whenClicked = whenClicked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean interaction(InventoryClickEvent event, FeatureGuiInventory featureInventory) {
|
||||
if (event.getSlot() == this.slot) {
|
||||
this.whenClicked.doSomething(event, featureInventory);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void creation(Inventory inventory) {
|
||||
inventory.setItem(this.slot, this.bukkitItem);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package me.samsuik.sakura.player.gui.components;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import me.samsuik.sakura.player.gui.FeatureGuiInventory;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@NullMarked
|
||||
public final class ItemSwitch implements GuiComponent {
|
||||
private final List<ItemStack> items;
|
||||
private final int slot;
|
||||
private final int selected;
|
||||
private final GuiClickEvent whenClicked;
|
||||
|
||||
public ItemSwitch(List<ItemStack> items, int slot, int selected, GuiClickEvent whenClicked) {
|
||||
Preconditions.checkArgument(!items.isEmpty());
|
||||
this.items = Collections.unmodifiableList(items);
|
||||
this.slot = slot;
|
||||
this.selected = selected;
|
||||
this.whenClicked = whenClicked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean interaction(InventoryClickEvent event, FeatureGuiInventory featureInventory) {
|
||||
if (this.slot == event.getSlot()) {
|
||||
int next = (this.selected + 1) % this.items.size();
|
||||
ItemSwitch itemSwitch = new ItemSwitch(this.items, this.slot, next, this.whenClicked);
|
||||
featureInventory.replaceComponent(this, itemSwitch);
|
||||
this.whenClicked.doSomething(event, featureInventory);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void creation(Inventory inventory) {
|
||||
inventory.setItem(this.slot, this.items.get(this.selected));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package me.samsuik.sakura.player.visibility;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jspecify.annotations.NonNull;
|
||||
|
||||
public final class PlayerVisibilitySettings implements VisibilitySettings {
|
||||
private static final String SETTINGS_COMPOUND_TAG = "clientVisibilitySettings";
|
||||
private final Reference2ObjectMap<VisibilityType, VisibilityState> visibilityStates = new Reference2ObjectOpenHashMap<>();
|
||||
|
||||
@Override
|
||||
public @NonNull VisibilityState get(@NonNull VisibilityType type) {
|
||||
VisibilityState state = this.visibilityStates.get(type);
|
||||
return state != null ? state : type.getDefault();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull VisibilityState set(@NonNull VisibilityType type, @NonNull VisibilityState state) {
|
||||
if (type.isDefault(state)) {
|
||||
this.visibilityStates.remove(type);
|
||||
} else {
|
||||
this.visibilityStates.put(type, state);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull VisibilityState currentState() {
|
||||
int modifiedCount = this.visibilityStates.size();
|
||||
if (modifiedCount == 0) {
|
||||
return VisibilityState.ON;
|
||||
} else if (modifiedCount != VisibilityTypes.types().size()) {
|
||||
return VisibilityState.MODIFIED;
|
||||
} else {
|
||||
return VisibilityState.OFF;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean playerModified() {
|
||||
return !this.visibilityStates.isEmpty();
|
||||
}
|
||||
|
||||
public void loadData(@NonNull CompoundTag tag) {
|
||||
if (!tag.contains(SETTINGS_COMPOUND_TAG, CompoundTag.TAG_COMPOUND)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompoundTag settingsTag = tag.getCompound(SETTINGS_COMPOUND_TAG);
|
||||
for (VisibilityType type : VisibilityTypes.types()) {
|
||||
if (settingsTag.contains(type.key(), CompoundTag.TAG_STRING)) {
|
||||
VisibilityState state = VisibilityState.valueOf(settingsTag.getString(type.key()));
|
||||
this.visibilityStates.put(type, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void saveData(@NonNull CompoundTag tag) {
|
||||
CompoundTag settingsTag = new CompoundTag();
|
||||
this.visibilityStates.forEach((t, s) -> settingsTag.putString(t.key(), s.name()));
|
||||
tag.put(SETTINGS_COMPOUND_TAG, settingsTag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package me.samsuik.sakura.player.visibility;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue;
|
||||
import me.samsuik.sakura.configuration.GlobalConfiguration;
|
||||
import me.samsuik.sakura.player.gui.FeatureGui;
|
||||
import me.samsuik.sakura.player.gui.FeatureGuiInventory;
|
||||
import me.samsuik.sakura.player.gui.components.ItemButton;
|
||||
import me.samsuik.sakura.player.gui.components.ItemSwitch;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
|
||||
import static me.samsuik.sakura.player.gui.ItemStackUtil.itemWithBlankName;
|
||||
|
||||
@NullMarked
|
||||
public final class VisibilityGui extends FeatureGui {
|
||||
private static final NamespacedKey TOGGLE_BUTTON_KEY = new NamespacedKey("sakura", "toggle_button");
|
||||
private static final NamespacedKey MENU_ITEMS_KEY = new NamespacedKey("sakura", "menu_items");
|
||||
|
||||
public VisibilityGui() {
|
||||
super(45, Component.text("FPS Settings"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillInventory(Inventory inventory) {
|
||||
for (int slot = 0; slot < inventory.getSize(); ++slot) {
|
||||
// x, y from top left of the inventory
|
||||
int x = slot % 9;
|
||||
int y = slot / 9;
|
||||
// from center
|
||||
int rx = x - 4;
|
||||
int ry = y - 2;
|
||||
double d = Math.sqrt(rx * rx + ry * ry);
|
||||
if (d <= 3.25) {
|
||||
inventory.setItem(slot, itemWithBlankName(GlobalConfiguration.get().fps.material));
|
||||
} else if (x % 8 == 0) {
|
||||
inventory.setItem(slot, itemWithBlankName(Material.BLACK_STAINED_GLASS_PANE));
|
||||
} else {
|
||||
inventory.setItem(slot, itemWithBlankName(Material.WHITE_STAINED_GLASS_PANE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterFill(Player player, FeatureGuiInventory inventory) {
|
||||
VisibilitySettings settings = player.getVisibility();
|
||||
IntArrayFIFOQueue slots = this.availableSlots();
|
||||
this.updateToggleButton(settings, player, inventory);
|
||||
for (VisibilityType type : VisibilityTypes.types()) {
|
||||
VisibilityState state = settings.get(type);
|
||||
int index = type.states().indexOf(state);
|
||||
int slot = slots.dequeueInt();
|
||||
|
||||
ItemSwitch itemSwitch = new ItemSwitch(
|
||||
VisibilityGuiItems.GUI_ITEMS.get(type),
|
||||
slot, index,
|
||||
(e, inv) -> {
|
||||
settings.cycle(type);
|
||||
this.updateToggleButton(settings, player, inv);
|
||||
}
|
||||
);
|
||||
|
||||
inventory.addComponent(itemSwitch, MENU_ITEMS_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateToggleButton(VisibilitySettings settings, Player player, FeatureGuiInventory inventory) {
|
||||
inventory.removeComponents(TOGGLE_BUTTON_KEY);
|
||||
VisibilityState settingsState = settings.currentState();
|
||||
ItemButton button = new ItemButton(
|
||||
VisibilityGuiItems.TOGGLE_BUTTON_ITEMS.get(settingsState),
|
||||
(2 * 9) + 8,
|
||||
(e, inv) -> {
|
||||
settings.toggleAll();
|
||||
inventory.removeAllComponents();
|
||||
this.afterFill(player, inv);
|
||||
}
|
||||
);
|
||||
inventory.addComponent(button, TOGGLE_BUTTON_KEY);
|
||||
}
|
||||
|
||||
private IntArrayFIFOQueue availableSlots() {
|
||||
IntArrayFIFOQueue slots = new IntArrayFIFOQueue();
|
||||
for (int row = 1; row < 4; ++row) {
|
||||
for (int column = 3; column < 6; ++column) {
|
||||
if ((column + row) % 2 == 0) {
|
||||
slots.enqueue((row * 9) + column);
|
||||
}
|
||||
}
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package me.samsuik.sakura.player.visibility;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
import me.samsuik.sakura.player.gui.ItemStackUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public final class VisibilityGuiItems {
|
||||
static final Reference2ObjectMap<VisibilityType, ImmutableList<ItemStack>> GUI_ITEMS = new Reference2ObjectOpenHashMap<>();
|
||||
static final Reference2ObjectMap<VisibilityState, ItemStack> TOGGLE_BUTTON_ITEMS = new Reference2ObjectOpenHashMap<>();
|
||||
|
||||
static {
|
||||
Reference2ObjectMap<VisibilityType, ItemStack> items = new Reference2ObjectOpenHashMap<>();
|
||||
|
||||
items.put(VisibilityTypes.TNT, ItemStackUtil.itemWithName(Material.TNT, Component.text("Tnt", NamedTextColor.RED)));
|
||||
items.put(VisibilityTypes.SAND, ItemStackUtil.itemWithName(Material.SAND, Component.text("Sand", NamedTextColor.YELLOW)));
|
||||
items.put(VisibilityTypes.EXPLOSIONS, ItemStackUtil.itemWithName(Material.COBWEB, Component.text("Explosions", NamedTextColor.WHITE)));
|
||||
items.put(VisibilityTypes.SPAWNERS, ItemStackUtil.itemWithName(Material.SPAWNER, Component.text("Spawners", NamedTextColor.DARK_GRAY)));
|
||||
items.put(VisibilityTypes.PISTONS, ItemStackUtil.itemWithName(Material.PISTON, Component.text("Pistons", NamedTextColor.GOLD)));
|
||||
|
||||
for (VisibilityType type : VisibilityTypes.types()) {
|
||||
ItemStack item = items.get(type);
|
||||
|
||||
ImmutableList<ItemStack> stateItems = type.states().stream()
|
||||
.map(s -> createItemForState(item, s))
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
|
||||
GUI_ITEMS.put(type, stateItems);
|
||||
}
|
||||
|
||||
TOGGLE_BUTTON_ITEMS.put(VisibilityState.ON, ItemStackUtil.itemWithName(Material.GREEN_STAINED_GLASS_PANE, Component.text("Enabled", NamedTextColor.GREEN)));
|
||||
TOGGLE_BUTTON_ITEMS.put(VisibilityState.MODIFIED, ItemStackUtil.itemWithName(Material.MAGENTA_STAINED_GLASS_PANE, Component.text("Modified", NamedTextColor.LIGHT_PURPLE)));
|
||||
TOGGLE_BUTTON_ITEMS.put(VisibilityState.OFF, ItemStackUtil.itemWithName(Material.RED_STAINED_GLASS_PANE, Component.text("Disabled", NamedTextColor.RED)));
|
||||
}
|
||||
|
||||
private static String lowercaseThenCapitalise(String name) {
|
||||
String lowercaseName = name.toLowerCase(Locale.ENGLISH);
|
||||
return StringUtils.capitalize(lowercaseName);
|
||||
}
|
||||
|
||||
private static ItemStack createItemForState(ItemStack in, VisibilityState state) {
|
||||
String capitalisedName = lowercaseThenCapitalise(state.name());
|
||||
Component component = Component.text(" | " + capitalisedName, NamedTextColor.GRAY);
|
||||
ItemStack itemCopy = in.clone();
|
||||
itemCopy.editMeta(m -> m.itemName(m.itemName().append(component)));
|
||||
return itemCopy;
|
||||
}
|
||||
}
|
||||
Submodule sakura-server/src/minecraft/java updated: 7a738b2575...51af06117f
Reference in New Issue
Block a user