Compare commits

...
2 Commits
Author SHA1 Message Date
NanaChiyo0721 2734c91fca Bug fixes
Shiroha CI / build (push) Canceled after 0s
Shiroha CI / Event File (push) Canceled after 0s
好罢有点多懒得总结了()
2026-08-15 01:58:11 +08:00
NanaChiyo0721 f317990af4 Correct wrong id generation in ReferenceCountingChunkLoader 2026-08-15 01:33:24 +08:00
7 changed files with 91 additions and 23 deletions
@@ -93,19 +93,35 @@ index 3ac52b025ac3e1a3f9135b8e593a385a847afe0f..d2c68c78d0a2f334169a92171081eeb4
+ // Shiroha end + // Shiroha end
} }
diff --git a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java diff --git a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
index b40da004b5281a041ee896ae176bfb9c4660f353..90ed5c2f1055b460472085f79a18af95fede5bb0 100644 index b40da004b5281a041ee896ae176bfb9c4660f353..32df97c0e5e886d2c7ab98506c8a950d4034b472 100644
--- a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java --- a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
+++ b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java +++ b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java
@@ -117,7 +117,9 @@ public class MoveToTargetSink extends Behavior<Mob> { @@ -52,7 +52,7 @@ public class MoveToTargetSink extends Behavior<Mob> {
Brain<?> brain = body.getBrain();
WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
boolean reachedTarget = this.reachedTarget(body, walkTarget);
- if (!reachedTarget && this.tryComputePath(body, walkTarget, level.getGameTime())) {
+ if (!reachedTarget && walkTarget.getTarget().checkThread(body.level()) && this.tryComputePath(body, walkTarget, level.getGameTime())) { // Shiroha - Fix region threading with entity ai data access
this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
return true;
}
@@ -108,7 +108,7 @@ public class MoveToTargetSink extends Behavior<Mob> {
if (newPath != null && this.lastTargetPos != null) {
WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get();
- if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0 && this.tryComputePath(body, walkTarget, level.getGameTime())) {
+ if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0 && walkTarget.getTarget().checkThread(body.level()) && this.tryComputePath(body, walkTarget, level.getGameTime())) { // Shiroha - Fix region threading with entity ai data access
this.lastTargetPos = walkTarget.getTarget().currentBlockPosition();
this.start(level, body, timestamp);
}
@@ -118,7 +118,6 @@ public class MoveToTargetSink extends Behavior<Mob> {
private boolean tryComputePath(final Mob body, final WalkTarget walkTarget, final long timestamp) { private boolean tryComputePath(final Mob body, final WalkTarget walkTarget, final long timestamp) {
BlockPos targetPos = walkTarget.getTarget().currentBlockPosition(); BlockPos targetPos = walkTarget.getTarget().currentBlockPosition();
+ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(body.level(), targetPos)) // Shiroha - Fix region threading with entity ai data access
this.path = body.getNavigation().createPath(targetPos, 0); this.path = body.getNavigation().createPath(targetPos, 0);
+ else this.path = null; // Shiroha - Fix region threading with entity ai data access - this.speedModifier = walkTarget.getSpeedModifier();
this.speedModifier = walkTarget.getSpeedModifier();
Brain<?> brain = body.getBrain(); Brain<?> brain = body.getBrain();
if (this.reachedTarget(body, walkTarget)) { if (this.reachedTarget(body, walkTarget)) {
brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE);
diff --git a/net/minecraft/world/entity/ai/behavior/PositionTracker.java b/net/minecraft/world/entity/ai/behavior/PositionTracker.java diff --git a/net/minecraft/world/entity/ai/behavior/PositionTracker.java b/net/minecraft/world/entity/ai/behavior/PositionTracker.java
index ce6cf5ecfb190428e3ef9b7dd39c98e3d27a7b9d..3eea48aad910760683e30594a9c1851aa17ce88c 100644 index ce6cf5ecfb190428e3ef9b7dd39c98e3d27a7b9d..3eea48aad910760683e30594a9c1851aa17ce88c 100644
--- a/net/minecraft/world/entity/ai/behavior/PositionTracker.java --- a/net/minecraft/world/entity/ai/behavior/PositionTracker.java
@@ -197,14 +213,14 @@ index 88a89b4c72cc99dc89d3f3cc928b2dfda4125759..b4bdc7eff97b058ee8ae2c9e4321b53c
public static <T> MemorySlot<T> create() { public static <T> MemorySlot<T> create() {
diff --git a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java diff --git a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
index e44814cfb6afb594456b8215bd13a92e93de2c85..c174330cc437e92a7221ff369fbf69da98d481dd 100644 index e44814cfb6afb594456b8215bd13a92e93de2c85..d4b65f13c137495d436b7e8133e0ce09ee2e08c0 100644
--- a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java --- a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java +++ b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java
@@ -60,6 +60,17 @@ public class FlyingPathNavigation extends PathNavigation { @@ -60,6 +60,17 @@ public class FlyingPathNavigation extends PathNavigation {
if (!this.isDone()) { if (!this.isDone()) {
Vec3 target = this.path.getNextEntityPos(this.mob); Vec3 target = this.path.getNextEntityPos(this.mob);
+ // Shiroha - Fix region threading with entity ai data access + // Shiroha start - Fix region threading with entity ai data access
+ if (io.nanachiyo0721.shiroha.config.modules.fixes.PathfindingFixesConfig.breakDownPathfindingWhenOutOfRegion) { + if (io.nanachiyo0721.shiroha.config.modules.fixes.PathfindingFixesConfig.breakDownPathfindingWhenOutOfRegion) {
+ // we assume that: + // we assume that:
+ // 1. The code above doesn't touch the 'main thread context' with the position from 'this.path' + // 1. The code above doesn't touch the 'main thread context' with the position from 'this.path'
@@ -219,7 +235,7 @@ index e44814cfb6afb594456b8215bd13a92e93de2c85..c174330cc437e92a7221ff369fbf69da
} }
} }
diff --git a/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/net/minecraft/world/entity/ai/navigation/PathNavigation.java diff --git a/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
index 2ea1ec39a37899ae1510d0036aa670e758077537..15c739ce4dc521b58811f72b11e557fe715d803a 100644 index 2ea1ec39a37899ae1510d0036aa670e758077537..3acafc2aa1219efb0d7a591b91afa42640bcd584 100644
--- a/net/minecraft/world/entity/ai/navigation/PathNavigation.java --- a/net/minecraft/world/entity/ai/navigation/PathNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/PathNavigation.java +++ b/net/minecraft/world/entity/ai/navigation/PathNavigation.java
@@ -188,6 +188,18 @@ public abstract class PathNavigation { @@ -188,6 +188,18 @@ public abstract class PathNavigation {
@@ -241,8 +257,39 @@ index 2ea1ec39a37899ae1510d0036aa670e758077537..15c739ce4dc521b58811f72b11e557fe
ProfilerFiller profiler = Profiler.get(); ProfilerFiller profiler = Profiler.get();
profiler.push("pathfind"); profiler.push("pathfind");
BlockPos fromPos = above ? this.mob.blockPosition().above() : this.mob.blockPosition(); BlockPos fromPos = above ? this.mob.blockPosition().above() : this.mob.blockPosition();
@@ -286,6 +298,17 @@ public abstract class PathNavigation {
if (!this.isDone()) {
Vec3 target = this.path.getNextEntityPos(this.mob);
+ // Shiroha start - Fix region threading with entity ai data access
+ if (io.nanachiyo0721.shiroha.config.modules.fixes.PathfindingFixesConfig.breakDownPathfindingWhenOutOfRegion) {
+ // we assume that:
+ // 1. The code above doesn't touch the 'main thread context' with the position from 'this.path'
+ // 2. The pathfinder could correctly recompute or discard the incorrect target position and this situation is happening rarely
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.mob.level(), target)) {
+ this.hasDelayedRecomputation = true;
+ return;
+ }
+ }
+ // Shiroha end
this.mob.getMoveControl().setWantedPosition(target.x, this.getGroundY(target), target.z, this.speedModifier);
}
}
diff --git a/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java b/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java
index 2b5ba8f01b57b5e54bba4a8624f17e1b550d818e..bf127e91354b784dc8f7db910c46e3f7c42eec67 100644
--- a/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java
+++ b/net/minecraft/world/entity/ai/navigation/WallClimberNavigation.java
@@ -44,7 +44,7 @@ public class WallClimberNavigation extends GroundPathNavigation {
super.tick();
} else {
if (this.pathToPosition != null) {
- if (!this.pathToPosition.closerToCenterThan(this.mob.position(), this.mob.getBbWidth())
+ if ((io.nanachiyo0721.shiroha.config.modules.fixes.PathfindingFixesConfig.breakDownPathfindingWhenOutOfRegion && ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.mob.level(), this.pathToPosition)) && !this.pathToPosition.closerToCenterThan(this.mob.position(), this.mob.getBbWidth()) // Shiroha - Fix region threading with entity ai data access
&& (
!(this.mob.getY() > this.pathToPosition.getY())
|| !BlockPos.containing(this.pathToPosition.getX(), this.mob.getY(), this.pathToPosition.getZ())
diff --git a/net/minecraft/world/entity/animal/allay/AllayAi.java b/net/minecraft/world/entity/animal/allay/AllayAi.java diff --git a/net/minecraft/world/entity/animal/allay/AllayAi.java b/net/minecraft/world/entity/animal/allay/AllayAi.java
index c3667e7997551e0f5ce63bc6ee890082d1431400..7caa2b2ee9b4063696d1bd80803d6ec84f24c1f5 100644 index c3667e7997551e0f5ce63bc6ee890082d1431400..edf2be5c1a48d80eaf900c782d02ea6f517fee2c 100644
--- a/net/minecraft/world/entity/animal/allay/AllayAi.java --- a/net/minecraft/world/entity/animal/allay/AllayAi.java
+++ b/net/minecraft/world/entity/animal/allay/AllayAi.java +++ b/net/minecraft/world/entity/animal/allay/AllayAi.java
@@ -112,6 +112,16 @@ public class AllayAi { @@ -112,6 +112,16 @@ public class AllayAi {
@@ -254,7 +301,7 @@ index c3667e7997551e0f5ce63bc6ee890082d1431400..7caa2b2ee9b4063696d1bd80803d6ec8
+ final BlockPos targetPos = position.pos(); + final BlockPos targetPos = position.pos();
+ +
+ // thread checks + // thread checks
+ if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(targetLevel, targetPos)) { + if (targetLevel == null || !ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(targetLevel, targetPos)) {
+ brain.eraseMemory(MemoryModuleType.LIKED_NOTEBLOCK_POSITION); // The memory value is not being belong to current tick region anymore + brain.eraseMemory(MemoryModuleType.LIKED_NOTEBLOCK_POSITION); // The memory value is not being belong to current tick region anymore
+ return Optional.empty(); + return Optional.empty();
+ } + }
@@ -29,10 +29,10 @@ index 9df6003d325536639706751c109c42eed7904f73..d89696bd0a0bd0b7c565b647ea97f24f
// for example, clearing of inventory after switching dimensions // for example, clearing of inventory after switching dimensions
this.postRemoveAfterChangingDimensions(); this.postRemoveAfterChangingDimensions();
diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java diff --git a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
index ab921ab4b4cc860abd77744f2bd201013e136e51..0ddf5ef000103b1ef88f3d3e232d734b2e198944 100644 index ab921ab4b4cc860abd77744f2bd201013e136e51..226198ae04bf53528dbeb6a45f6cfb6f42342a36 100644
--- a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java --- a/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
+++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java +++ b/net/minecraft/world/entity/boss/enderdragon/EnderDragon.java
@@ -1011,4 +1011,12 @@ public class EnderDragon extends Mob implements Enemy { @@ -1011,4 +1011,16 @@ public class EnderDragon extends Mob implements Enemy {
return 500; return 500;
} }
// Paper end - init expToDrop for already dying spawned dragon // Paper end - init expToDrop for already dying spawned dragon
@@ -40,7 +40,11 @@ index ab921ab4b4cc860abd77744f2bd201013e136e51..0ddf5ef000103b1ef88f3d3e232d734b
+ // Shiroha start - Fix dragon part desync + // Shiroha start - Fix dragon part desync
+ public void syncDragonPartsAfterTeleportTransform() { + public void syncDragonPartsAfterTeleportTransform() {
+ for (EnderDragonPart part : this.subEntities) { + for (EnderDragonPart part : this.subEntities) {
+ this.tickPart(part, 0.0, 0.0, 0.0); // offset -> 0.0 + this.tickPart(part,
+ Math.abs(part.getBoundingBox().maxX - part.getBoundingBox().minX),
+ Math.abs(part.getBoundingBox().maxY - part.getBoundingBox().minY),
+ Math.abs(part.getBoundingBox().maxZ - part.getBoundingBox().minZ)
+ ); // offset -> 0.0
+ } + }
+ } + }
+ // Shiroha end + // Shiroha end
@@ -8,7 +8,7 @@ As part of: Leaf (https://github.com/Winds-Studio/Leaf/blob/7f3e240bbe0970683c40
Licensed under: GPL-3.0 (https://github.com/Winds-Studio/Leaf/blob/7f3e240bbe0970683c40279a7a65f0fde47503b6/licenses/GPL-3.0.txt) Licensed under: GPL-3.0 (https://github.com/Winds-Studio/Leaf/blob/7f3e240bbe0970683c40279a7a65f0fde47503b6/licenses/GPL-3.0.txt)
diff --git a/net/minecraft/server/commands/SeedCommand.java b/net/minecraft/server/commands/SeedCommand.java diff --git a/net/minecraft/server/commands/SeedCommand.java b/net/minecraft/server/commands/SeedCommand.java
index 86ee3b3ae028597576b9549bc4954dfcbaa6732c..56bab28e8fcae16a4e71527afb73e5794098723e 100644 index 86ee3b3ae028597576b9549bc4954dfcbaa6732c..4a4f45b02a6ff32065a521552f50cba2a2bdd1ff 100644
--- a/net/minecraft/server/commands/SeedCommand.java --- a/net/minecraft/server/commands/SeedCommand.java
+++ b/net/minecraft/server/commands/SeedCommand.java +++ b/net/minecraft/server/commands/SeedCommand.java
@@ -13,6 +13,15 @@ public class SeedCommand { @@ -13,6 +13,15 @@ public class SeedCommand {
@@ -18,7 +18,7 @@ index 86ee3b3ae028597576b9549bc4954dfcbaa6732c..56bab28e8fcae16a4e71527afb73e579
+ // Leaf start - Matter - SecureSeed Command + // Leaf start - Matter - SecureSeed Command
+ if (io.nanachiyo0721.shiroha.config.modules.function.SecureSeedConfig.enabled) { + if (io.nanachiyo0721.shiroha.config.modules.function.SecureSeedConfig.enabled) {
+ su.plo.matter.Globals.setupGlobals(c.getSource().getLevel()); + su.plo.matter.Globals.setupGlobals(c.getSource().getLevel());
+ String seedStr = su.plo.matter.Globals.seedToString(su.plo.matter.Globals.worldSeed); + String seedStr = su.plo.matter.Globals.seedToString(su.plo.matter.Globals.worldSeed.get());
+ Component featureSeedComponent = ComponentUtils.copyOnClickText(seedStr); + Component featureSeedComponent = ComponentUtils.copyOnClickText(seedStr);
+ +
+ c.getSource().sendSuccess(() -> Component.translatable(("Feature seed: %s"), featureSeedComponent), false); + c.getSource().sendSuccess(() -> Component.translatable(("Feature seed: %s"), featureSeedComponent), false);
@@ -1,14 +1,33 @@
package io.nanachiyo0721.shiroha.config.modules.optimizations; package io.nanachiyo0721.shiroha.config.modules.optimizations;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.mojang.logging.LogUtils;
import io.nanachiyo0721.shiroha.config.IConfigModule; import io.nanachiyo0721.shiroha.config.IConfigModule;
import io.nanachiyo0721.shiroha.config.flags.ConfigClassInfo; import io.nanachiyo0721.shiroha.config.flags.ConfigClassInfo;
import io.nanachiyo0721.shiroha.config.flags.ConfigInfo; import io.nanachiyo0721.shiroha.config.flags.ConfigInfo;
import io.nanachiyo0721.shiroha.config.flags.DoNotLoad;
import io.nanachiyo0721.shiroha.enums.EnumConfigCategory; import io.nanachiyo0721.shiroha.enums.EnumConfigCategory;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.util.Set;
@ConfigClassInfo(category = EnumConfigCategory.OPTIMIZATIONS, name = "reduce_sensor_work", comments = "When it is enabled, it will delete the line of sight cache less often and use a faster nearby comparison.") @ConfigClassInfo(category = EnumConfigCategory.OPTIMIZATIONS, name = "reduce_sensor_work", comments = "When it is enabled, it will delete the line of sight cache less often and use a faster nearby comparison.")
public class PetalReduceSensorWorkConfig implements IConfigModule { public class PetalReduceSensorWorkConfig implements IConfigModule {
@ConfigInfo(name = "enabled") @ConfigInfo(name = "enabled")
public static boolean enabled = true; public static boolean enabled = true;
@ConfigInfo(name = "delay_ticks", comments = "The interval of each entity to drop the cache(in ticks)") @ConfigInfo(name = "delay_ticks", comments = "The interval of each entity to drop the cache(in ticks (>0))")
public static int delayTicks = 10; public static int delayTicks = 10;
@DoNotLoad
private static final Logger LOGGER = LogUtils.getClassLogger();
@Override
public void onLoaded(CommentedFileConfig configInstance, @Nullable Set<Exception> e) {
if (delayTicks <= 0) {
LOGGER.info("Too small delay_ticks was set for reduce_sensor_work(expected > 0 but got {}), Clamping back to 1!", delayTicks);
delayTicks = 1;
}
}
} }
@@ -254,9 +254,7 @@ public class ReferenceCountingChunkLoader {
private static final class ReferenceCountData { private static final class ReferenceCountData {
private static final AtomicLong ID_GENERATOR = new AtomicLong(); private final long id = ChunkTaskScheduler.getNextChunkLoadId();
private final long id = ID_GENERATOR.getAndIncrement();
public final AtomicLong referenceCount = new AtomicLong(1L); public final AtomicLong referenceCount = new AtomicLong(1L);
public final AtomicLong referenceTTL = new AtomicLong(MAX_CHUNK_TTL_TICKS); public final AtomicLong referenceTTL = new AtomicLong(MAX_CHUNK_TTL_TICKS);
@@ -12,7 +12,7 @@ public class Globals {
public static final int WORLD_SEED_LONGS = 16; public static final int WORLD_SEED_LONGS = 16;
public static final int WORLD_SEED_BITS = WORLD_SEED_LONGS * 64; public static final int WORLD_SEED_BITS = WORLD_SEED_LONGS * 64;
public static final long[] worldSeed = new long[WORLD_SEED_LONGS]; public static final ThreadLocal<long[]> worldSeed = ThreadLocal.withInitial(() -> new long[WORLD_SEED_LONGS]);
public static final ThreadLocal<Integer> dimension = ThreadLocal.withInitial(() -> 0); public static final ThreadLocal<Integer> dimension = ThreadLocal.withInitial(() -> 0);
public enum Salt { public enum Salt {
@@ -40,7 +40,7 @@ public class Globals {
if (!SecureSeedConfig.enabled) return; if (!SecureSeedConfig.enabled) return;
long[] seed = world.worldGenSettings.options().featureSeed(); long[] seed = world.worldGenSettings.options().featureSeed();
System.arraycopy(seed, 0, worldSeed, 0, WORLD_SEED_LONGS); System.arraycopy(seed, 0, worldSeed.get(), 0, WORLD_SEED_LONGS);
int worldIndex = Iterables.indexOf(world.getServer().levelKeys(), it -> it == world.dimension()); int worldIndex = Iterables.indexOf(world.getServer().levelKeys(), it -> it == world.dimension());
if (worldIndex == -1) if (worldIndex == -1)
worldIndex = world.getServer().levelKeys().size(); // if we are in world construction it may not have been added to the map yet worldIndex = world.getServer().levelKeys().size(); // if we are in world construction it may not have been added to the map yet
@@ -44,7 +44,7 @@ public class WorldgenCryptoRandom extends WorldgenRandom {
return; return;
} }
System.arraycopy(Globals.worldSeed, 0, this.worldSeed, 0, Globals.WORLD_SEED_LONGS); System.arraycopy(Globals.worldSeed.get(), 0, this.worldSeed, 0, Globals.WORLD_SEED_LONGS);
message[0] = ((long) x << 32) | ((long) z & 0xffffffffL); message[0] = ((long) x << 32) | ((long) z & 0xffffffffL);
message[1] = ((long) Globals.dimension.get() << 32) | ((long) salt & 0xffffffffL); message[1] = ((long) Globals.dimension.get() << 32) | ((long) salt & 0xffffffffL);
message[2] = typeSalt.ordinal(); message[2] = typeSalt.ordinal();
@@ -100,7 +100,7 @@ public class WorldgenCryptoRandom extends WorldgenRandom {
WorldgenCryptoRandom fork = new WorldgenCryptoRandom(0, 0, null, 0); WorldgenCryptoRandom fork = new WorldgenCryptoRandom(0, 0, null, 0);
System.arraycopy(Globals.worldSeed, 0, fork.worldSeed, 0, Globals.WORLD_SEED_LONGS); System.arraycopy(Globals.worldSeed.get(), 0, fork.worldSeed, 0, Globals.WORLD_SEED_LONGS);
fork.message[0] = this.message[0]; fork.message[0] = this.message[0];
fork.message[1] = this.message[1]; fork.message[1] = this.message[1];
fork.message[2] = this.message[2]; fork.message[2] = this.message[2];