Skip to content

feat: 完善类主世界重置与天体装置联动 - #4512

Open
WhereisFff wants to merge 2 commits into
Anvil-Dev:dev/1.21/1.6from
WhereisFff:dev/1.21/CFA4
Open

feat: 完善类主世界重置与天体装置联动#4512
WhereisFff wants to merge 2 commits into
Anvil-Dev:dev/1.21/1.6from
WhereisFff:dev/1.21/CFA4

Conversation

@WhereisFff

Copy link
Copy Markdown
Contributor

- 允许玩家通过入口请求并激活炸毁后类主世界的新世代,完成旧维度卸载、数据清理与玩家转移
- 在服务器启动和维度重建时重建世界边界监听,避免新世代遗漏主世界边界设置
- 让传送门和直接传送路径在重置待命期间正确排队,并在世界可用后传送至安全落点
- 根据红石信号平滑降低天体锻造砧转速,修正玩家头颅天体缩放时的渲染高度与包围盒
- 修复新放置红石导线未立即索引相邻侦测器的问题,并将大型激光器强制标记为实心方块以防水
Copilot AI lite review requested due to automatic review settings August 22, 2026 20:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

cd /tmp && python3 -c "
import zipfile
z = zipfile.ZipFile('/tmp/minecraft-server-1.21.1.jar')
names = z.namelist()
print('已混淆 1.21.1 server jar(无映射类名)')
# 检查是否已映射
mapped = [n for n in names if 'Borde...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

cd /tmp && echo "=== Linkie search getListeners (mojang 1.21.1) ===" && curl -s "https://linkie.shedaniel.dev/api/1.21.1/search?namespace=mojang&query=getListeners" | head -c 2000; echo; echo "=== Dir...

Reason: Security scan — [MEDIUM] Lookalike TLD detected: Domain uses '.dev' TLD which can be confused with file extensions; [MEDIUM] Lookalike TLD detected: Domain uses '.dev' TLD which can be confused with file extensions

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

which jar java python3; echo "---"; cd /tmp && timeout 100 python3 -c "
import zipfile
z = zipfile.ZipFile('minecraft-server-1.21.1.jar')
names = z.namelist()
print('total entries:', len(names))
print...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

The linkie API request timed out and got blocked. I'll skip the online verification and rely on local knowledge of 1.21.1 vanilla. Let me check whether there is a way to verify getListeners from the repo itself — AnvilCraft must have compile-time references, or maybe there's a checkable way via gradle. But since the build isn't set up, let me instead check the mixin target validity via the local clone's usage patterns and known 1.21.1 sources.

Actually — the practical way: WorldBorder.getListeners() — I am fairly confident this private method exists in 1.21.1 (mojmap). Let me double check by recalling the 1.21.1 WorldBorder class structure:

  • Fields: listeners (List), plus border settings
  • addListener(BorderChangeListener) — no duplicate-type check in 1.21.1? Let me recall... I believe the "already added" check was added in a later version (1.20.5+ or 1.21.x). Actually the throw-on-duplicate check if (this.listeners.stream().anyMatch((p) -> p.getClass() == borderChangeListener.getClass())) — hmm. This is actually in WorldBorder.addListener as of 1.20.1? Let me recall the actual code:
public void addListener(BorderChangeListener borderChangeListener) {
    if (this.listeners.stream().anyMatch((p) -> p.getClass() == borderChangeListener.getClass())) {
        throw new IllegalStateException("Listener of this type is already added");
    }
    this.listeners.add(borderChangeListener);
}

Yes! I'm quite sure this is present in 1.20.1+ (it was added in 1.16-ish? Actually I remember it being there for a long time — the check that prevents duplicate listener types). This is why the "absolute border" bug exists when mixing datapack-dimension mods...

So the virtual code does have this check. This means: if two DelegateBorderChangeListener instances exist on the overworld border, adding another throws. The configure flow does unlink-then-add, so it's safe as long as unlink removes vanilla's listener.

But wait — CRITICAL: at createLevels RETURN, if vanilla's loop already added its own DelegateBorderChangeListener for the overworld-like level, then:

  1. configureOverworldLikeBorder(server)unlinkOverworldLikeBorder(server, level) removes the vanilla listener (worldBorder matches level.getWorldBorder())
  2. addListener(new DelegateBorderChangeListener(...)) — no conflict

BUT — there's a subtle issue: at what point in createLevels is the vanilla listener added? If it's added in the level-creation loop (per dimension), then by RETURN all listeners exist. The unlink works. ✓

However — there's another subtle problem. unlinkOverworldLikeBorder removes ALL listeners whose worldBorder == level.getWorldBorder(). That's fine. But is DelegateBorderChangeListener.worldBorder the field name? The BorderChangeListener.DelegateBorderChangeListener constructor takes the WorldBorder. The field — in mojmap it's worldBorder. Yes, I'm confident: private final WorldBorder worldBorder; in BorderChangeListener$DelegateBorderChangeListener.

And WorldBorder.getListeners() private method returning this.listeners — mojmap private method name getListeners. I'm now fairly confident, because there was a 1.19.4+ change adding WorldBorder#getListeners used by... hmm, actually I recall the invoker pattern for WorldBorder listeners from other mods! There are multiple mods that access WorldBorder's private listener list via @Invoker("getListeners") — yes, this is a well-known mixin target. E.g., the "borders" mods, "worldborder" datapack tools. I'm confident getListeners() exists as a private method in 1.21.1.

Also CopyOnWriteArrayList — I'm confident WorldBorder uses COW for listeners (it's documented that world border listeners can be modified during notification without CME). Actually hold on, let me reconsider. In 1.21.1:

private final List<BorderChangeListener> listeners = new CopyOnWriteArrayList<>();

I believe this specifically because the vanilla code in WorldBorder iterates this.listeners in methods like onAbsoluteMaxSizeChange etc. and MinecraftServer adds/removes listeners dynamically (the removeListener is called in unloadLevel? no...). Vanilla addListener is only called in createLevels; but NeoForge might too. COW is the safe choice and I remember seeing it. OK.

Given the uncertain network access, I'll proceed with the analysis; my review can note that the mixin invoker targets can't be fully verified here, but they match known vanilla 1.21.1 structure. Actually — let me try one more quick thing locally: check if the AnvilCraft project has any vendored minecraft source or a way to check. Let me also look at the actual getListeners used in another mixin in this repo (maybe there's precedent). Also verify how vanilla 1.21.1 MinecraftServer.createLevels registers border listeners — I recall in 1.21.1:

private void createLevels(ChunkProgressListener progressListener) {
    ...
    this.overworld = new ServerLevel(...);
    if (this.worldData.worldGenOptions().bonusChest()) { ... }
    ...
    for (Map.Entry<ResourceKey<LevelStem>, LevelStem> entry : levelStemRegistry.entrySet()) {
        ResourceKey<LevelStem> resourcekey = entry.getKey();
        if (resourcekey == LevelStem.OVERWORLD) continue;
        ResourceKey<Level> resourcekey1 = ResourceKey.create(Registries.DIMENSION, resourcekey.location());
        DerivedLevelData derivedleveldata = new DerivedLevelData(this.worldData, this.worldData.overworldData());
        ServerLevel serverlevel = new ServerLevel(this, Util.backgroundExecutor(), this.storageSource, derivedleveldata, resourcekey1, entry.getValue(), progressListener, this.worldData.isDebugWorld(), BiomeManager.obfuscateSeed(this.worldData.worldGenOptions().seed()), ImmutableList.of(), false, this.overworld.getRandomSequences());
        this.forgeGetWorldMap().put(resourcekey1, serverlevel);
        this.registerWorldBorderListeners(serverlevel);
        this.forceTicks.put(resourcekey1, ...);
    }
    ...
}

private void registerWorldBorderListeners(ServerLevel level) {
    if (this.overworld != null) {
        this.overworld.getWorldBorder().addListener(new BorderChangeListener.DelegateBorderChangeListener(level.getWorldBorder()));
    }
}

I'm confident registerWorldBorderListeners exists in 1.21.1 (it was added when the border sync to dimensions was introduced). Yes — in 1.19.x+ vanilla syncs world border to all dimensions. So vanilla already adds a DelegateBorderChangeListener for the overworld-like level at startup.

Then the PR's createLevels-RETURN inject + unlink-then-add is redundant at startup (vanilla already does it), but it becomes necessary for the "activation" path (where the level is replaced outside createLevels). At activation, the new level's border has no listener → vanilla would never add one → so configureOverworldLikeBorder(server, replacement) fixes the actual bug. ✓ The startup inject is a belt-and-suspenders measure. Fine — but worth noting "unlink+add at every startup is idempotent" as a positive.

BUT WAIT — there's a real problem: the addListener duplicate-type check! At startup:

  • vanilla loop adds listener A (DelegateBorderChangeListener for overworld-like border)
  • PR's RETURN inject: unlink removes A, then adds listener B. OK — no duplicate.
    But what if the overworld-like level is NOT created in the vanilla loop (e.g. registered later, or created lazily)? Then at RETURN, server.getLevel(...) = null → configure no-op. And later when the level is created... who adds the listener? The activatePendingGeneration path adds it when the level is replaced. But if the level is created fresh on world load through some other path (the initial creation prior to any reset)...

Hmm, wait. How is the overworld-like level initially created? Through vanilla createLevels loop (since ModifyArgs intercepts the ServerLevel constructor for it, the dimension IS in the level stem registry at startup). So:

  1. Server start: vanilla createLevels → creates overworld-like level (via registry stem) + registerWorldBorderListeners ✓. PR's RETURN inject unlinks vanilla listener + re-adds equivalent one — harmless, idempotent.
  2. Collapse → reset pending → entry request → activatePendingGeneration: unlink (the listener from startup pointing at oldLevel's border) → close old → create new level → put in map → configureOverworldLikeBorder: unlink (nothing) → add new listener for new border ✓ + applySettings from overworld border ✓.

This all works.

Now, one more edge — in unlinkOverworldLikeBorder(server, oldLevel) at activation: the vanilla (original) listener points at oldLevel's border → removed ✓. Good — otherwise old listener would keep syncing settins to a closed level's border (harmless but wrong).

OK. Now let me also verify BorderChangeListener.DelegateBorderChangeListener has a public constructor (it does — new DelegateBorderChangeListener(WorldBorder) is public — vanilla uses it in MinecraftServer). ✓

WorldBorder.applySettings(WorldBorder.Settings) — public ✓, returns void. ✓

server.overworld().getWorldBorder() ✓.

Now — the truly concerning issue remains the teleportTo for queued players and the moveQueuedDirectEntries teleporting into the new dimension while it's still generating spawn chunks. findSafeLandingPos(destination, fallback) — checks for safe spot; if the new level's spawn chunks are still generating (SILENT listener, background executor), getSharedSpawnPos() returns the spawn pos (from DerivedLevelData), but the chunks may not exist yet → findSafeLandingPos might return null or an unsafe pos → fallback = spawn pos → teleport into possibly-empty/corrupted chunk area. ServerLevel has a getChunkAt...? The teleport would force-load the chunk — fine, it just generates it. Entities teleported will wait. Probably OK.

But hold on — one real bug candidate: moveQueuedDirectEntries is called AFTER syncStateToAll, in the SAME tick as activation. The new level was JUST created (constructor ran). Spawn chunks generating async. Teleporting a player into a chunk that's still being generated is generally OK (Forge/vanilla handles it).

Now, the teleportTo rotation: player.teleportTo(destination, x, y, z, yRot, xRot) — exists (base forceReturn uses same signature ✓).

Now the biggest remaining question: requestGenerationByEntry gating + the getEntryDestination returning null during RESET_PENDING means portal users are NOT auto-teleported after activation (only queued via onPlayerChangedDimension). PR description claims "让传送门和直接传送路径在重置待命期间正确排队,并在世界可用后传送至安全落点" — hmm, "传送门...排队". But PORTAL users (tryLand path) are NOT queued anywhere! They get getEntryDestination → null → tryLand false → NOT teleported, NOT queued. The only thing that happens is requestGenerationByEntry(). So after activation, the portal user standing there would need to step through again. Is that "排队"? Not really — but the portal becomes ready (they requested activation). The described "在世界可用后传送至安全落点" (auto-teleport to safe landing after world available) only happens for onPlayerChangedDimension (direct teleport path) queued players.

Hmm wait, actually, let me re-read: "让传送门和直接传送路径在重置待命期间正确排队,并在世界可用后传送至安全落点". Perhaps the intended behavior:

  • 传送门 (portal/tryLand path): during RESET_PENDING → entry denied → requests generation → when activated, portal reopens (they can walk through again).
  • 直接传送路径 (direct changeDimension): player ends up IN the dimension via some bypass → forceReturn + queued → auto-teleported to safe landing spot after world available.

The word "排队" fits the direct path well (QUEUED_DIRECT_ENTRIES). For the portal path, "正确排队" might loosely mean the request is queued. This is a description-vs-implementation nuance — worth one note (⚠️ or 💡).

Another important check: tickLandingPortal dropped the landing check but the entity scan calls tryTouchTeleport → tryLand → getEntryDestination → null during RESET_PENDING → no teleport. ButtryTouchTeleport early-returns if (entity.isOnPortalCooldown()) return false;? No wait, that's in tryLand. In tryTouchTeleport: the entity touching the portal repeatedly — tryLand is called each scan; during RESET_PENDING, requestGenerationByEntry() called each time — idempotent (flag already set, setDirty not re-called). ✓

Now — What about a player standing INSIDE the overworld-like dimension when the collapse begins? They get hurt + forceReturn. Fine.

What about onPlayerLoggedIn during RESET_PENDING? base:

if (hasPendingForcedRespawn || phase != ACTIVE && isInOverworldLike) {
    addPendingForcedRespawn; forceReturn;
} else if (isInOverworldLike) { markPlayerInOverworldLike; }

If a player logs in during RESET_PENDING while in the OVERWORLD dimension (not overworld-like), the first condition: hasPendingForcedRespawn || (phase != ACTIVE && isInOverworldLike) — if phase is RESET_PENDING but player is in overworld: first cond false (no pending, not in OWL), else-if false → nothing. Fine.

If a player logs in IN the overworld-like dimension during RESET_PENDING: phase != ACTIVE && isInOverworldLike true → addPendingForcedRespawn + forceReturn ✓ (they get bounced out). Note: NOT queued (onPlayerLoggedIn doesn't queue). So players who log in inside during RESET_PENDING get bounced but not queued for the new gen — minor inconsistency with onPlayerChangedDimension behavior, but reasonable (they never "entered" via a portal).

OK. Now, collapsing-tick restructure: the old early-return if (phase != COLLAPSING) return; was removed and replaced with if-block. The new RESET_PENDING activation block runs after. Equivalent for COLLAPSING. ✓ But — subtle: in the OLD code, when phase != COLLAPSING, tick returned immediately. In the NEW code, when phase == ACTIVE: first if false, second if false → nothing. ✓ Equivalent.

Now the collapseDamageIssued + markResetPending flow: in COLLAPSING, when allOnlinePlayersOutsideOverworldLike → markResetPending → phase = RESET_PENDING. Then the entry request can trigger activation. ✓

But wait — there's a GAP: markResetPending only happens in tick() while phase==COLLAPSING. If a server restarts while phase==COLLAPSING (persisted), tick() resumes collapse damage etc. Fine.

One more potential issue: activatePendingGeneration posts LevelEvent.Unload(oldLevel) and LevelEvent.Load(replacement) manually. Some Forge hooks (e.g., ServerLevel capabilities, StorageSource management) might double-handle. It matches the event contract; reasonable.

**A significant concern: oldLevel.close() may throw or the level may be referenced by server.getWorldData()... Also server.getLevel() during the gap (after close, before put) — same thread, nothing queries between. ✓

**And: deleteDimensionStorage after close — if close() doesn't flush pending saves... oldLevel.close() in 1.21.1 does this.chunkSource.close() which flushes chunk writes. Then delete folder. Since no player is in the level and only the world being deleted, fine.

Actually — wait. One serious question: does closing the old level and deleting the dimension folder break anything for the OVERWORLD being the same folder? No — the overworld-like dimension folder is a SEPARATE subfolder (dim/...). Deleting it is fine. The manifest is written to worldRoot/data/ — separate. ✓

Now the rotation float change:

  • rotation/preRotation int→float. Where are they used?
    • getRotation()/getPreRotation() — used in renderer for interpolation: float rotation = Mth.lerp(partialTick, blockEntity.getPreRotation(), blockEntity.getRotation())? Need to check renderer usage of getRotation. If renderer does blockEntity.getRotation() and uses as float ✓.
    • The == 360>= 360 - 360 change is needed for float.
    • Overflow protection: rotation -= 360.0f — if rotation were exactly 360.01, becomes 0.01 ✓. But if rotation somehow jumped to 720+ (impossible with +3 per tick increments), it'd only subtract once — fine.
    • preRotation also float ✓.
  • The speed formula: rotationSpeed = 3.0f / (1.0f + getRedstoneSignal() * 0.4f). At signal 0: 3.0 deg/tick (same as before ✓). At signal 15: 3.0/7.0 ≈ 0.4286 deg/tick — smooth reduction ✓. At signal 1: 3/1.4 ≈ 2.14. This matches "根据红石信号平滑降低转速". [Bug] 带有玩家头颅星球的锻星砧放大化后头颅位置不合理 #4511 mentions redstone.

Now, is getRedstoneSignal() computed per tick — reads comparator/redstone around? In BE tick — it's a method on the BE (base exists, since renderer uses it: blockEntity.getRedstoneSignal()). ✓

Where is rotation used in CelestialForgingAnvilBlockEntity? Check for int usage elsewhere e.g., bodyRotation still int (this.bodyRotation += 1; — bodyRotation field is int, unchanged ✓). Need to verify rotation isn't passed somewhere expecting int (e.g., getRotation() callers). Let me grep the base for getRotation() usage in renderer/sync.

Also check: is rotation/preRotation used in SyncProxy or serialization (e.g., setRotation)? Let me grep.

Let me also double-check CelestialForgingAnvilBlockEntity — the diff hunk this.rotation >= 360.0f this.rotation -= 360.0f. With rotationSpeed possibly being fractional, rotation never hits exactly 360 → the old == 360 would never fire with floats; new >= 360 → -= 360 correct. ✓ But — precision: rotation could accumulate fractional; after many wraps, rotation -= 360 — if rotation = 360.00003f (from drift), becomes 0.00003 — fine. If rotation were 359.99997 and never >= 360... it does reach 360 eventually? 359.99997 + 0.4286 = 360.4286 → >= 360 → -= 360 → 0.4286. ✓ wraps properly.

One concern: getPreRotation() (int→float) is likely used in renderer for lerp between preRotation and rotation. Since both float now, fine.

Let me quickly check usage of getRotation/getPreRotation in the codebase (any int consumer like % 360 or int cast).

Also — RedstoneWireNetworkManager.rememberAdjacentObservers:

  • Called from topologyChanged BEFORE requestTopologyUpdate.
  • rememberAdjacentObservers iterates 6 directions, checks level.hasChunkAt (no chunk load ✓) + getBlockState(...).is(Blocks.OBSERVER).
  • Records observers via rememberObserver(observerPos) — which presumably records the observer position (and observerChunks).
  • Potential issue: topologyChanged is called when a wire is placed — but also when wires are REMOVED? The method name "topologyChanged" — called from where? Let me check callers. If it's called on removal too, rememberAdjacentObservers would index observers adjacent to a REMOVED wire — observers that are no longer adjacent to any wire. Hmm — but the indexing happens during topology rebuild; the observer set is later refined per-network. Might cause a temporarily stale observer entry, but refreshObserver handles it. Need to check callers of topologyChanged.
  • Another issue: requestTopologyUpdate is async (queued for the network manager tick)? If so, rememberAdjacentObservers records observers at CALL time, but the topology update runs later — the wire's neighbors at call time = correct for the just-placed wire. ✓ The point of the fix: on wire placement, neighbor updates fire before the topology build indexes the NEW wire's adjacency to existing observers; pre-recording ensures observers get indexed even if they'd be missed. Reasonable.

Let me check topologyChanged callers and requestTopologyUpdate + rememberObserver implementation in base.

Also ModBlocks large_laser forceSolidOn: .forceSolidOn() — BlockBehaviour.Properties.forceSolidOn() exists ✓ (1.21). Solid blocks prevent water flow — is that the intended fix? #4490 "将大型激光器强制标记为实心方块以防水" — yes, makes it solid so water doesn't flow through. But note: solid also affects culling (it's noOcclusion so no visual issue), suffocation (isSuffocating(never) overrides), spawning (isValidSpawn never), redstone conduction — a solid block conducts redstone? BlockBehaviour default isRedstoneConductor returns true for solid... wait — forceSolidOn() sets isSolid = true via... let me recall: BlockBehaviour.Properties.forceSolidOn():

public Properties forceSolidOn() {
    this.forceSolidOn = true;
    return this;
}

and the block material setting makes isSolid() true (affects: canBeReplacedByFluids? waterlogging? redstone?). Actually isSolid() (BlockState) controls: fluid flow direction (solid blocks block flowing water), and isRedstoneConductor() (which uses isSolid? no — isRedstoneConductor uses isSolidRender? hmm). Fine — the water fix intent. A tradeoff: solid blocks block pistons? No. They prevent water/lava flow. OK.

Hmm, one consideration: making LargeLaser solid may change redstone wire behavior on top and mob spawning (overridden) — minor. Fine.

Now — the mixin JSON additions: accessor.DelegateBorderChangeListenerAccessor and accessor.WorldBorderAccessor added ✓ (in alphabetical order — actually list shows DelegateBorderChangeListenerAccessor after CropBlockAccessor — alphabetical ✓; WorldBorderAccessor after VaultServerDataAccessor ✓).

MinecraftServerMixin: new @Inject(method = "createLevels", at = @At("RETURN")) with (ChunkProgressListener listener, CallbackInfo ci) — createLevels returns Map — CallbackInfo for non-void is allowed (non-cancellable). ✓ Mixin allows CallbackInfo in non-void method handlers? Hmm — actually, I need to be careful: Mixin REQUIRES CallbackInfoReturnable for methods with a return value? No — Mixin docs: "CallbackInfoReturnable is required when the target method has a return value AND you need to return/cancel; for simple observability CallbackInfo works". Actually, hmm. Let me think hard. Mixin's @Inject handler parameter validation: the handler must end with CallbackInfo or CallbackInfoReturnable, and if the target method is non-void, you MUST use CallbackInfoReturnable?

I believe the actual rule: Mixin will apply either, but for non-void methods using CallbackInfo, the inject works as long as cancellable is false. There are many real-world examples of @Inject(method = "createLevels", at = @At("RETURN")) private void on(CallbackInfo ci) on a method returning Map... Hmm, actually wait. Let me recall the exact Mixin validator: InjectionInfo.validate checks the callback info type matches: "CallbackInfo is not valid for methods with a return value"?

From Mixin source: CallbackInfoInjector... The validation in InjectionPoint/InjectorInjectMethodInfo: there's a check validateReturnType — for @Inject, the handler's callback parameter CAN be CallbackInfo for non-void methods; the framework only enforces CallbackInfoReturnable when the handler declares it. It does NOT enforce return-value methods to use CIR. I've definitely seen @Inject(at=@At("HEAD")) void method(CallbackInfo ci) on non-void methods — very common. So ✓ fine.

Actually — one more consideration on the RETURN inject: createLevels at RETURN — at that point this.overworld is set and all levels created. configureOverworldLikeBorder(server) — uses server.overworld() ✓ non-null.

Hmm — BUT, there's a potential stack/config issue: the existing HEAD inject calls OverworldLikeGenerationBootstrap.prepare(server, storageAccess) which populates PREPARED_GENERATIONS. The RETURN inject calls configureOverworldLikeBorder(server)server.getLevel(OVERWORLD_LIKE_LEVEL) — if the level was created by the vanilla loop, non-null ✓.

Edge: createLevels might be called again? No, once.

OK — I also want to check OverworldLikeGenerationBootstrap.prepare in base to understand PREPARED_GENERATIONS structure and confirm PreparedGeneration record fields (manifest, resetPerformed). Not strictly needed.

Now let me verify the remaining API existence locally: grep base for getRotation() / preRotation consumers, topologyChanged callers, rememberObserver, requestTopologyUpdate semantics, and CelestialForgingAnvilBlockEntity.getRedstoneSignal. Also verify celestial renderer's usage of getRotation (float compat).

Let me run those gre

@Gugle2308

Copy link
Copy Markdown
Collaborator

💾 Self-improvement review: Patched references/celestial-world-reset-review.md in skill 'minecraft-mod-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown
Collaborator

⚠️ Dangerous command requires approval:

curl -sL "https://maven.neoforged.net/releases/net/neoforged/neoforge/21.1.226/neoforge-21.1.226-userdev.jar" -o /tmp/userdev.zip 2>/dev/null && ls -la /tmp/userdev.zip && cd /tmp && python3 -c "
impo...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown
Collaborator

↻ Thinking-only response — prefilling to continue (1/2)

3 similar comments
@Gugle2308

Copy link
Copy Markdown
Collaborator

↻ Thinking-only response — prefilling to continue (1/2)

@Gugle2308

Copy link
Copy Markdown
Collaborator

↻ Thinking-only response — prefilling to continue (1/2)

@Gugle2308

Copy link
Copy Markdown
Collaborator

↻ Thinking-only response — prefilling to continue (1/2)

@Gugle2308

Copy link
Copy Markdown
Collaborator

⏱️ Rate limited. Waiting 600.0s (attempt 2/3)...

@Gugle2308

Copy link
Copy Markdown
Collaborator

⏳ Retrying in 2.1s (attempt 1/3)...

@Gugle2308

Copy link
Copy Markdown
Collaborator

↻ Thinking-only response — prefilling to continue (1/2)

@Gugle2308

Copy link
Copy Markdown
Collaborator

⏳ Retrying in 2.9s (attempt 1/3)...

@Gugle2308

Copy link
Copy Markdown
Collaborator

⏳ Retrying in 5.0s (attempt 2/3)...

@Gugle2308

Copy link
Copy Markdown
Collaborator

⏳ Retrying in 2.1s (attempt 1/3)...

@Gugle2308

Copy link
Copy Markdown
Collaborator

⏳ Retrying in 4.5s (attempt 2/3)...

@Gugle2308

Copy link
Copy Markdown
Collaborator

❌ API failed after 3 retries — Connection error.

@Gugle2308

Copy link
Copy Markdown
Collaborator

API call failed after 3 retries: Connection error.

Copilot AI review requested due to automatic review settings August 23, 2026 09:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Gugle2308

Copy link
Copy Markdown
Collaborator

⏳ Retrying in 2.0s (attempt 1/3)...

@Gugle2308

Copy link
Copy Markdown
Collaborator

⏳ Retrying in 4.7s (attempt 2/3)...

@Gugle2308

Copy link
Copy Markdown
Collaborator

❌ API failed after 3 retries — HTTP 503: Service temporarily unavailable

@Gugle2308

Copy link
Copy Markdown
Collaborator

⏳ Retrying in 2.1s (attempt 1/3)...

@Gugle2308

Copy link
Copy Markdown
Collaborator

⏳ Retrying in 5.4s (attempt 2/3)...

@Gugle2308

Copy link
Copy Markdown
Collaborator

❌ API failed after 3 retries — HTTP 503: Service temporarily unavailable

@Gugle2308

Copy link
Copy Markdown
Collaborator

API call failed after 3 retries: HTTP 503: Service temporarily unavailable

1 similar comment
@Gugle2308

Copy link
Copy Markdown
Collaborator

API call failed after 3 retries: HTTP 503: Service temporarily unavailable

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants