mirror of
https://github.com/Winds-Studio/Leaf.git
synced 2025-12-28 19:39:17 +00:00
Update changes from ver/1.21.4 branch
This commit is contained in:
@@ -4,10 +4,11 @@ import io.papermc.paper.PaperBootstrap;
|
||||
import joptsimple.OptionSet;
|
||||
|
||||
public class LeafBootstrap {
|
||||
|
||||
public static final boolean enableFMA = Boolean.parseBoolean(System.getProperty("Leaf.enableFMA", "false")); // Leaf - FMA feature
|
||||
|
||||
public static void boot(final OptionSet options) {
|
||||
runPreBootTasks();
|
||||
//runPreBootTasks();
|
||||
|
||||
PaperBootstrap.boot(options);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.dreeam.leaf.async;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dreeam.leaf.async.ai.AsyncGoalThread;
|
||||
import org.dreeam.leaf.async.path.AsyncPathProcessor;
|
||||
import org.dreeam.leaf.async.tracker.MultithreadedTracker;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ShutdownExecutors {
|
||||
public static final Logger LOGGER = LogManager.getLogger("Leaf");
|
||||
|
||||
public static void shutdown(MinecraftServer server) {
|
||||
if (server.mobSpawnExecutor != null) {
|
||||
LOGGER.info("Waiting for mob spawning thread to shutdown...");
|
||||
try {
|
||||
server.mobSpawnExecutor.join(3000L);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (AsyncPlayerDataSaving.IO_POOL != null) {
|
||||
LOGGER.info("Waiting for player I/O executor to shutdown...");
|
||||
AsyncPlayerDataSaving.IO_POOL.shutdown();
|
||||
try {
|
||||
AsyncPlayerDataSaving.IO_POOL.awaitTermination(60L, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (server.asyncGoalThread != null) {
|
||||
LOGGER.info("Waiting for mob target finding thread to shutdown...");
|
||||
AsyncGoalThread.RUNNING = false;
|
||||
try {
|
||||
server.asyncGoalThread.join(3000L);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (MultithreadedTracker.TRACKER_EXECUTOR != null) {
|
||||
LOGGER.info("Waiting for mob tracker executor to shutdown...");
|
||||
MultithreadedTracker.TRACKER_EXECUTOR.shutdown();
|
||||
try {
|
||||
MultithreadedTracker.TRACKER_EXECUTOR.awaitTermination(10L, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (AsyncPathProcessor.pathProcessingExecutor != null) {
|
||||
LOGGER.info("Waiting for mob pathfinding executor to shutdown...");
|
||||
AsyncPathProcessor.pathProcessingExecutor.shutdown();
|
||||
try {
|
||||
AsyncPathProcessor.pathProcessingExecutor.awaitTermination(10L, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
public class AsyncGoalThread extends Thread {
|
||||
|
||||
public static volatile boolean RUNNING = true;
|
||||
public AsyncGoalThread(final MinecraftServer server) {
|
||||
super(() -> run(server), "Leaf Async Goal Thread");
|
||||
this.setDaemon(false);
|
||||
@@ -18,7 +19,7 @@ public class AsyncGoalThread extends Thread {
|
||||
}
|
||||
|
||||
private static void run(MinecraftServer server) {
|
||||
while (server.isRunning()) {
|
||||
while (RUNNING) {
|
||||
boolean retry = false;
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
var exec = level.asyncGoalExecutor;
|
||||
|
||||
@@ -28,7 +28,7 @@ public class AsyncPathProcessor {
|
||||
private static final String THREAD_PREFIX = "Leaf Async Pathfinding";
|
||||
private static final Logger LOGGER = LogManager.getLogger(THREAD_PREFIX);
|
||||
private static long lastWarnMillis = System.currentTimeMillis();
|
||||
private static final ThreadPoolExecutor pathProcessingExecutor = new ThreadPoolExecutor(
|
||||
public static final ThreadPoolExecutor pathProcessingExecutor = new ThreadPoolExecutor(
|
||||
1,
|
||||
AsyncPathfinding.asyncPathfindingMaxThreads,
|
||||
AsyncPathfinding.asyncPathfindingKeepalive, TimeUnit.SECONDS,
|
||||
|
||||
@@ -30,7 +30,7 @@ public class MultithreadedTracker {
|
||||
private static final String THREAD_PREFIX = "Leaf Async Tracker";
|
||||
private static final Logger LOGGER = LogManager.getLogger(THREAD_PREFIX);
|
||||
private static long lastWarnMillis = System.currentTimeMillis();
|
||||
private static ThreadPoolExecutor TRACKER_EXECUTOR = null;
|
||||
public static ThreadPoolExecutor TRACKER_EXECUTOR = null;
|
||||
|
||||
private MultithreadedTracker() {
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.dreeam.leaf.config.modules.async;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
import org.dreeam.leaf.config.LeafConfig;
|
||||
|
||||
public class AsyncTargetFinding extends ConfigModules {
|
||||
|
||||
@@ -33,6 +34,11 @@ public class AsyncTargetFinding extends ConfigModules {
|
||||
asyncTargetFindingInitialized = true;
|
||||
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
// Disable if parallel world ticking is enabled, as they are incompatible.
|
||||
if (enabled && SparklyPaperParallelWorldTicking.enabled) {
|
||||
LeafConfig.LOGGER.warn("Async Target Finding is incompatible with Parallel World Ticking. Disabling Async Target Finding automatically.");
|
||||
enabled = false;
|
||||
}
|
||||
alertOther = config.getBoolean(getBasePath() + ".async-alert-other", true);
|
||||
searchBlock = config.getBoolean(getBasePath() + ".async-search-block", true);
|
||||
searchEntity = config.getBoolean(getBasePath() + ".async-search-entity", true);
|
||||
|
||||
@@ -17,7 +17,7 @@ public class SparklyPaperParallelWorldTicking extends ConfigModules {
|
||||
public static boolean logContainerCreationStacktraces = false;
|
||||
public static boolean disableHardThrow = false;
|
||||
@Deprecated
|
||||
public static boolean runAsyncTasksSync = false;
|
||||
public static Boolean runAsyncTasksSync;
|
||||
// STRICT, BUFFERED, DISABLED
|
||||
public static String asyncUnsafeReadHandling = "BUFFERED";
|
||||
|
||||
@@ -52,15 +52,14 @@ public class SparklyPaperParallelWorldTicking extends ConfigModules {
|
||||
asyncUnsafeReadHandling = "DISABLED";
|
||||
}
|
||||
|
||||
runAsyncTasksSync = config.getBoolean(getBasePath() + ".run-async-tasks-sync", false); // Default to false now
|
||||
if (runAsyncTasksSync) {
|
||||
LeafConfig.LOGGER.warn("The setting '{}.run-async-tasks-sync' is deprecated. Use 'async-unsafe-read-handling: STRICT' for similar safety checks or 'BUFFERED' for buffered reads.", getBasePath());
|
||||
// Transfer old config
|
||||
runAsyncTasksSync = config.getBoolean(getBasePath() + ".run-async-tasks-sync");
|
||||
if (runAsyncTasksSync != null && runAsyncTasksSync) {
|
||||
LeafConfig.LOGGER.warn("The setting '{}.run-async-tasks-sync' is deprecated, removed automatically. Use 'async-unsafe-read-handling: BUFFERED' for buffered reads instead.", getBasePath());
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
LeafConfig.LOGGER.info("Using {} threads for Parallel World Ticking", threads);
|
||||
}
|
||||
|
||||
runAsyncTasksSync = enabled && runAsyncTasksSync; // Auto-disable if main feature is off
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ public class Knockback extends ConfigModules {
|
||||
public static boolean canPlayerKnockbackZombie = true;
|
||||
@Experimental
|
||||
public static boolean flushKnockback = false;
|
||||
public static boolean oldBlastProtectionKnockbackBehavior = false;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
@@ -34,5 +35,6 @@ public class Knockback extends ConfigModules {
|
||||
"使玩家可以击退僵尸."
|
||||
));
|
||||
flushKnockback = config.getBoolean(getBasePath() + ".flush-location-while-knockback-player", flushKnockback);
|
||||
oldBlastProtectionKnockbackBehavior = config.getBoolean(getBasePath() + ".old-blast-protection-explosion-knockback", oldBlastProtectionKnockbackBehavior);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package org.dreeam.leaf.config.modules.gameplay;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
import org.dreeam.leaf.config.annotations.Experimental;
|
||||
|
||||
public class SmoothTeleport extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.GAMEPLAY.getBaseKeyName() + ".smooth-teleport";
|
||||
}
|
||||
|
||||
@Experimental
|
||||
public static boolean enabled = false;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath(), enabled, config.pickStringRegionBased("""
|
||||
**Experimental feature**
|
||||
Whether to make a "smooth teleport" when players changing dimension.
|
||||
This requires original world and target world have same logical height to work.""",
|
||||
"""
|
||||
**实验性功能**
|
||||
是否在玩家切换世界时尝试使用 "平滑传送".
|
||||
此项要求源世界和目标世界逻辑高度相同才会生效."""
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ public class ProtocolSupport extends ConfigModules {
|
||||
public static boolean doABarrelRollForceEnabled = false;
|
||||
public static boolean doABarrelRollForceInstalled = false;
|
||||
public static int doABarrelRollInstalledTimeout = 40;
|
||||
public static DoABarrelRollPackets.KineticDamage doABarrelRollKineticDamage = DoABarrelRollPackets.KineticDamage.VANILLA;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
@@ -53,7 +52,6 @@ public class ProtocolSupport extends ConfigModules {
|
||||
doABarrelRollForceEnabled = config.getBoolean(getBasePath() + ".do-a-barrel-roll-force-enabled", doABarrelRollForceEnabled);
|
||||
doABarrelRollForceInstalled = config.getBoolean(getBasePath() + ".do-a-barrel-roll-force-installed", doABarrelRollForceInstalled);
|
||||
doABarrelRollInstalledTimeout = config.getInt(getBasePath() + ".do-a-barrel-roll-installed-timeout", 0);
|
||||
doABarrelRollKineticDamage = DoABarrelRollPackets.KineticDamage.valueOf(config.getString(getBasePath() + ".do-a-barrel-roll-kinetic-damage", doABarrelRollKineticDamage.name()));
|
||||
if (doABarrelRollInstalledTimeout <= 0) {
|
||||
doABarrelRollInstalledTimeout = 40;
|
||||
}
|
||||
@@ -63,7 +61,7 @@ public class ProtocolSupport extends ConfigModules {
|
||||
doABarrelRollForceEnabled,
|
||||
doABarrelRollForceInstalled,
|
||||
doABarrelRollInstalledTimeout,
|
||||
doABarrelRollKineticDamage
|
||||
DoABarrelRollPackets.KineticDamage.VANILLA
|
||||
);
|
||||
} else {
|
||||
DoABarrelRollProtocol.deinit();
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class BrainRunningBehaviorCacheUpdate extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName();
|
||||
}
|
||||
|
||||
public static int interval = 5;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
interval = config.getInt(getBasePath() + ".entity-running-behavior-cache-update-interval", interval,
|
||||
config.pickStringRegionBased(
|
||||
"How often entity update current brain running behavior list.",
|
||||
"生物更新现有 Brain Behavior 列表缓存的间隔."));
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class OptimiseBlockEntities extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName();
|
||||
}
|
||||
|
||||
public static boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath() + ".optimise-block-entities", enabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class OptimizeBiome extends ConfigModules {
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName() + ".cache-biome";
|
||||
}
|
||||
|
||||
public static boolean enabled = false;
|
||||
public static boolean mobSpawn = false;
|
||||
public static boolean advancement = false;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config().getBoolean(getBasePath() + ".enabled", enabled);
|
||||
mobSpawn = config.getBoolean(getBasePath() + ".mob-spawning", false);
|
||||
advancement = config.getBoolean(getBasePath() + ".advancements", false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class OptimizeBlockEntities extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName();
|
||||
}
|
||||
|
||||
public static boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
// Transfer old config
|
||||
Boolean optimiseBlockEntities = config.getBoolean(getBasePath() + ".optimise-block-entities");
|
||||
if (optimiseBlockEntities != null && optimiseBlockEntities) {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
enabled = config.getBoolean(getBasePath() + ".optimize-block-entities", enabled);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,5 @@ public class OptimizePlayerMovementProcessing extends ConfigModules {
|
||||
Whether to optimize player movement processing by skipping unnecessary edge checks and avoiding redundant view distance updates.""",
|
||||
"""
|
||||
是否优化玩家移动处理,跳过不必要的边缘检查并避免冗余的视距更新。"""));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class PreloadNaturalMobSpawning extends ConfigModules {
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName() + ".preload-mob-spawning-position";
|
||||
}
|
||||
|
||||
public static boolean enabled = false;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ public class ReduceChunkSourceUpdates extends ConfigModules {
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled,
|
||||
config.pickStringRegionBased(
|
||||
"Reduces chunk source updates on inter-chunk player moves. (Recommended to enable)",
|
||||
"Reduces chunk source updates on inter-chunk player moves.",
|
||||
"减少玩家跨区块移动时的区块源更新。"
|
||||
)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class ThrottleNaturalMobSpawning extends ConfigModules {
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName() + ".throttle-mob-spawning";
|
||||
}
|
||||
|
||||
public static boolean enabled = false;
|
||||
public static long[] failedAttempts;
|
||||
public static int[] spawnChance;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath() + ".enabled", enabled);
|
||||
MobCategory[] categories = MobCategory.values();
|
||||
failedAttempts = new long[categories.length];
|
||||
spawnChance = new int[categories.length];
|
||||
for (int i = 0; i < categories.length; i++) {
|
||||
String category = getBasePath() + "." + categories[i].getSerializedName();
|
||||
long attempts = config.getLong(category + ".min-failed", 8);
|
||||
double chance = config.getDouble(category + ".spawn-chance", 25.0);
|
||||
|
||||
failedAttempts[i] = Math.max(-1, attempts);
|
||||
spawnChance[i] = Math.clamp(0, (int) Math.round(chance * 10.24), 1024);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package org.dreeam.leaf.config.modules.opt;
|
||||
|
||||
import org.dreeam.leaf.config.ConfigModules;
|
||||
import org.dreeam.leaf.config.EnumConfigCategory;
|
||||
|
||||
public class VT4DownloadPool extends ConfigModules {
|
||||
|
||||
public String getBasePath() {
|
||||
return EnumConfigCategory.PERF.getBaseKeyName();
|
||||
}
|
||||
|
||||
public static boolean enabled = true;
|
||||
|
||||
@Override
|
||||
public void onLoaded() {
|
||||
enabled = config.getBoolean(getBasePath() + ".use-virtual-thread-for-download-pool", enabled,
|
||||
config.pickStringRegionBased(
|
||||
"Use the new Virtual Thread introduced in JDK 21 for download worker pool.",
|
||||
"是否为下载工作线程池使用虚拟线程(如果可用)。"));
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,7 @@
|
||||
package org.dreeam.leaf.protocol;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2BooleanMaps;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2FloatMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2FloatMaps;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2FloatOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.*;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
@@ -25,7 +18,6 @@ import org.dreeam.leaf.protocol.DoABarrelRollPackets.KineticDamage;
|
||||
import org.dreeam.leaf.protocol.DoABarrelRollPackets.ModConfigServer;
|
||||
import org.dreeam.leaf.protocol.DoABarrelRollPackets.RollSyncC2SPacket;
|
||||
import org.dreeam.leaf.protocol.DoABarrelRollPackets.RollSyncS2CPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.bukkit.event.player.PlayerKickEvent;
|
||||
|
||||
import java.util.List;
|
||||
@@ -54,8 +46,8 @@ public class DoABarrelRollProtocol implements Protocol {
|
||||
private ModConfigServer config = DEFAULT;
|
||||
private boolean configUpdated = false;
|
||||
|
||||
private final Reference2ReferenceMap<ServerGamePacketListenerImpl, ClientInfo> syncStates = new Reference2ReferenceOpenHashMap<>();
|
||||
private final Reference2ReferenceMap<ServerGamePacketListenerImpl, DelayedRunnable> scheduledKicks = new Reference2ReferenceOpenHashMap<>();
|
||||
private final Reference2ReferenceMap<ServerGamePacketListenerImpl, ClientInfo> syncStates = Reference2ReferenceMaps.synchronize(new Reference2ReferenceOpenHashMap<>());
|
||||
private final Reference2ReferenceMap<ServerGamePacketListenerImpl, DelayedRunnable> scheduledKicks = Reference2ReferenceMaps.synchronize(new Reference2ReferenceOpenHashMap<>());
|
||||
public final Reference2BooleanMap<ServerGamePacketListenerImpl> isRollingMap = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>());
|
||||
public final Reference2FloatMap<ServerGamePacketListenerImpl> rollMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap<>());
|
||||
public final Reference2BooleanMap<ServerGamePacketListenerImpl> lastIsRollingMap = Reference2BooleanMaps.synchronize(new Reference2BooleanOpenHashMap<>());
|
||||
@@ -99,14 +91,14 @@ public class DoABarrelRollProtocol implements Protocol {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(ServerPlayer player, @NotNull LeafCustomPayload payload) {
|
||||
public void handle(ServerPlayer player, LeafCustomPayload payload) {
|
||||
switch (payload) {
|
||||
case ConfigUpdateC2SPacket ignored ->
|
||||
player.connection.send(Protocols.createPacket(new ConfigUpdateAckS2CPacket(PROTOCOL_VERSION, false)));
|
||||
case ConfigResponseC2SPacket configResponseC2SPacket -> {
|
||||
var reply = clientReplied(player.connection, configResponseC2SPacket);
|
||||
if (reply == HandshakeState.RESEND) {
|
||||
sendHandshake(player);
|
||||
sendHandshake(player.connection);
|
||||
}
|
||||
}
|
||||
case RollSyncC2SPacket rollSyncC2SPacket -> {
|
||||
@@ -138,41 +130,39 @@ public class DoABarrelRollProtocol implements Protocol {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tickTracker(ServerPlayer player) {
|
||||
if (!isRollingMap.containsKey(player.connection)) {
|
||||
public void tickPlayer(ServerPlayer player) {
|
||||
ServerGamePacketListenerImpl connection = player.connection;
|
||||
if (getHandshakeState(connection).state == HandshakeState.NOT_SENT) {
|
||||
sendHandshake(connection);
|
||||
}
|
||||
if (!isRollingMap.containsKey(connection)) {
|
||||
return;
|
||||
}
|
||||
if (!isRollingMap.getBoolean(connection)) {
|
||||
rollMap.put(connection, 0.0F);
|
||||
}
|
||||
|
||||
var isRolling = isRollingMap.getBoolean(player.connection);
|
||||
var roll = rollMap.getFloat(player.connection);
|
||||
var lastIsRolling = lastIsRollingMap.getBoolean(player.connection);
|
||||
var lastRoll = lastRollMap.getFloat(player.connection);
|
||||
boolean isRolling = isRollingMap.getBoolean(connection);
|
||||
float roll = rollMap.getFloat(connection);
|
||||
boolean lastIsRolling = lastIsRollingMap.getBoolean(connection);
|
||||
float lastRoll = lastRollMap.getFloat(connection);
|
||||
if (isRolling == lastIsRolling && roll == lastRoll) {
|
||||
return;
|
||||
}
|
||||
var payload = new RollSyncS2CPacket(player.getId(), isRolling, roll);
|
||||
var packet = Protocols.createPacket(payload);
|
||||
for (ServerPlayerConnection seenBy : player.moonrise$getTrackedEntity().seenBy()) {
|
||||
var tracked = player.moonrise$getTrackedEntity();
|
||||
if (tracked == null) {
|
||||
return;
|
||||
}
|
||||
for (ServerPlayerConnection seenBy : tracked.seenBy()) {
|
||||
if (seenBy instanceof ServerGamePacketListenerImpl conn
|
||||
&& getHandshakeState(conn).state == HandshakeState.ACCEPTED) {
|
||||
seenBy.send(packet);
|
||||
}
|
||||
}
|
||||
lastIsRollingMap.put(player.connection, isRolling);
|
||||
lastRollMap.put(player.connection, roll);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tickPlayer(ServerPlayer player) {
|
||||
if (getHandshakeState(player.connection).state == HandshakeState.NOT_SENT) {
|
||||
sendHandshake(player);
|
||||
}
|
||||
if (!isRollingMap.containsKey(player.connection)) {
|
||||
return;
|
||||
}
|
||||
if (!isRollingMap.getBoolean(player.connection)) {
|
||||
rollMap.put(player.connection, 0.0F);
|
||||
}
|
||||
lastIsRollingMap.put(connection, isRolling);
|
||||
lastRollMap.put(connection, roll);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -190,7 +180,7 @@ public class DoABarrelRollProtocol implements Protocol {
|
||||
if (configUpdated) {
|
||||
configUpdated = false;
|
||||
for (ServerPlayer player : server.getPlayerList().players) {
|
||||
sendHandshake(player);
|
||||
sendHandshake(player.connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,9 +189,9 @@ public class DoABarrelRollProtocol implements Protocol {
|
||||
return config.forceInstalled() ? OptionalInt.of(config.installedTimeout()) : OptionalInt.empty();
|
||||
}
|
||||
|
||||
private void sendHandshake(ServerPlayer player) {
|
||||
player.connection.send(Protocols.createPacket(initiateConfigSync(player.connection)));
|
||||
configSentToClient(player.connection);
|
||||
private void sendHandshake(ServerGamePacketListenerImpl connection) {
|
||||
connection.send(Protocols.createPacket(initiateConfigSync(connection)));
|
||||
configSentToClient(connection);
|
||||
}
|
||||
|
||||
private void configSentToClient(ServerGamePacketListenerImpl handler) {
|
||||
@@ -255,7 +245,7 @@ public class DoABarrelRollProtocol implements Protocol {
|
||||
return info.state;
|
||||
}
|
||||
|
||||
private boolean isLimited(ServerGamePacketListenerImpl net) {
|
||||
private boolean isLimited(ServerGamePacketListenerImpl ignore) {
|
||||
return true;
|
||||
// return net.getPlayer().getBukkitEntity().hasPermission(DoABarrelRoll.MODID + ".configure");
|
||||
}
|
||||
@@ -266,19 +256,19 @@ public class DoABarrelRollProtocol implements Protocol {
|
||||
|
||||
private ConfigSyncS2CPacket initiateConfigSync(ServerGamePacketListenerImpl handler) {
|
||||
var isLimited = isLimited(handler);
|
||||
getHandshakeState(handler).isLimited = isLimited;
|
||||
// getHandshakeState(handler).isLimited = isLimited;
|
||||
return new ConfigSyncS2CPacket(PROTOCOL_VERSION, config, isLimited, isLimited ? DEFAULT : config);
|
||||
}
|
||||
|
||||
private static class ClientInfo {
|
||||
private HandshakeState state;
|
||||
private int protocolVersion;
|
||||
private boolean isLimited;
|
||||
// private boolean isLimited;
|
||||
|
||||
private ClientInfo(HandshakeState state, int protocolVersion, boolean isLimited) {
|
||||
private ClientInfo(HandshakeState state, int protocolVersion, boolean ignore) {
|
||||
this.state = state;
|
||||
this.protocolVersion = protocolVersion;
|
||||
this.isLimited = isLimited;
|
||||
// this.isLimited = isLimited;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.dreeam.leaf.protocol;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -19,9 +18,7 @@ interface Protocol {
|
||||
|
||||
void tickPlayer(ServerPlayer player);
|
||||
|
||||
void tickTracker(ServerPlayer player);
|
||||
|
||||
void disconnected(ServerPlayer conn);
|
||||
|
||||
void handle(ServerPlayer player, @NotNull LeafCustomPayload payload);
|
||||
void handle(ServerPlayer player, LeafCustomPayload payload);
|
||||
}
|
||||
|
||||
@@ -61,12 +61,6 @@ public class Protocols {
|
||||
}
|
||||
}
|
||||
|
||||
public static void tickTracker(ServerPlayer player) {
|
||||
for (Protocol protocol : PROTOCOLS) {
|
||||
protocol.tickTracker(player);
|
||||
}
|
||||
}
|
||||
|
||||
public static void disconnected(ServerPlayer conn) {
|
||||
for (Protocol protocol : PROTOCOLS) {
|
||||
protocol.disconnected(conn);
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
package org.dreeam.leaf.util.list;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import java.util.AbstractList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Spliterator;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A specialized list that allows for efficient hiding and showing of elements
|
||||
* without physically removing them from the backing store.
|
||||
* <p>
|
||||
* Iteration only processes "visible" elements, and visibility can be toggled in O(1) time.
|
||||
* This is useful for managing lists of tasks or objects where a large set exists,
|
||||
* but only a small subset is active at any given time.
|
||||
*
|
||||
* @param <E> The type of elements in this list.
|
||||
*/
|
||||
public class ActivationList<E> extends AbstractList<E> {
|
||||
|
||||
private final ObjectArrayList<E> elements;
|
||||
private final BitSet visibilityMask;
|
||||
private final Object2IntOpenHashMap<E> elementToIndexMap;
|
||||
private final boolean isVisibleByDefault;
|
||||
private int removedSlotCount;
|
||||
|
||||
/**
|
||||
* Constructs a new, empty MaskedList.
|
||||
*
|
||||
* @param isVisibleByDefault The default visibility for elements added to this list.
|
||||
*/
|
||||
public ActivationList(boolean isVisibleByDefault) {
|
||||
this.elements = new ObjectArrayList<>();
|
||||
this.visibilityMask = new BitSet();
|
||||
this.elementToIndexMap = new Object2IntOpenHashMap<>();
|
||||
this.elementToIndexMap.defaultReturnValue(-1);
|
||||
this.isVisibleByDefault = isVisibleByDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new, empty MaskedList with default visibility set to true.
|
||||
*/
|
||||
public ActivationList() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an element to the list or, if it already exists, updates its visibility.
|
||||
*
|
||||
* @param element The element to add or update.
|
||||
* @param visible The desired visibility of the element.
|
||||
*/
|
||||
public void addOrUpdate(E element, boolean visible) {
|
||||
int index = this.elementToIndexMap.getInt(element);
|
||||
if (index == -1) {
|
||||
index = this.elements.size();
|
||||
this.elements.add(element);
|
||||
this.elementToIndexMap.put(element, index);
|
||||
}
|
||||
this.visibilityMask.set(index, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the visibility of an existing element.
|
||||
*
|
||||
* @param element The element whose visibility to change.
|
||||
* @param visible True to make the element visible, false to hide it.
|
||||
*/
|
||||
public void setVisibility(E element, boolean visible) {
|
||||
int index = this.elementToIndexMap.getInt(element);
|
||||
if (index != -1) {
|
||||
this.visibilityMask.set(index, visible);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E element) {
|
||||
if (this.elementToIndexMap.containsKey(element)) {
|
||||
throw new IllegalArgumentException("MaskedList cannot contain duplicate elements: " + element);
|
||||
}
|
||||
this.addOrUpdate(element, this.isVisibleByDefault);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
int index = this.elementToIndexMap.removeInt(o);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.visibilityMask.clear(index);
|
||||
this.elements.set(index, null);
|
||||
this.removedSlotCount++;
|
||||
|
||||
if (this.removedSlotCount > 0 && this.removedSlotCount * 2 >= this.elements.size()) {
|
||||
compact();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds the internal list (wow)
|
||||
*/
|
||||
private void compact() {
|
||||
int writeIndex = 0;
|
||||
for (int readIndex = 0; readIndex < this.elements.size(); readIndex++) {
|
||||
E element = this.elements.get(readIndex);
|
||||
|
||||
if (element != null) {
|
||||
if (readIndex != writeIndex) {
|
||||
this.elements.set(writeIndex, element);
|
||||
this.elementToIndexMap.put(element, writeIndex);
|
||||
this.visibilityMask.set(writeIndex, this.visibilityMask.get(readIndex));
|
||||
}
|
||||
writeIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
int oldSize = this.elements.size();
|
||||
if (writeIndex < oldSize) {
|
||||
this.elements.removeElements(writeIndex, oldSize);
|
||||
this.visibilityMask.clear(writeIndex, oldSize);
|
||||
}
|
||||
|
||||
this.removedSlotCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.visibilityMask.cardinality();
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index) {
|
||||
if (index < 0 || index >= this.size()) {
|
||||
throw new IndexOutOfBoundsException("Index: " + index + ", Visible Size: " + this.size());
|
||||
}
|
||||
|
||||
int setBitIndex = -1;
|
||||
for (int i = 0; i <= index; i++) {
|
||||
setBitIndex = this.visibilityMask.nextSetBit(setBitIndex + 1);
|
||||
}
|
||||
return this.elements.get(setBitIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new MaskedIterator();
|
||||
}
|
||||
|
||||
private class MaskedIterator implements Iterator<E> {
|
||||
private int nextVisibleIndex;
|
||||
|
||||
MaskedIterator() {
|
||||
this.nextVisibleIndex = ActivationList.this.visibilityMask.nextSetBit(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.nextVisibleIndex != -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
E element = ActivationList.this.elements.get(this.nextVisibleIndex);
|
||||
this.nextVisibleIndex = ActivationList.this.visibilityMask.nextSetBit(this.nextVisibleIndex + 1);
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<E> spliterator() {
|
||||
return new MaskedSpliterator();
|
||||
}
|
||||
|
||||
private class MaskedSpliterator implements Spliterator<E> {
|
||||
private int currentIndex;
|
||||
|
||||
MaskedSpliterator() {
|
||||
this.currentIndex = ActivationList.this.visibilityMask.nextSetBit(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super E> action) {
|
||||
if (this.currentIndex != -1) {
|
||||
action.accept(ActivationList.this.elements.get(this.currentIndex));
|
||||
this.currentIndex = ActivationList.this.visibilityMask.nextSetBit(this.currentIndex + 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spliterator<E> trySplit() {
|
||||
return null; // This spliterator does not support splitting.
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimateSize() {
|
||||
return ActivationList.this.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int characteristics() {
|
||||
return Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.SIZED;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user