mirror of
https://github.com/GTNewHorizons/ForgeMultipart.git
synced 2026-11-13 17:04:25 +00:00
refactor: port multipart event handler
Preserve event-bus identity and the annotated singleton surface.
This commit is contained in:
1 parent
e9f244fefa
commit
4cd63eb3c8
7 files changed
+194
-95
No files matched your search
@@ -755,3 +755,10 @@ generated-trait compatibility work. Scala-runtime removal remains a later projec
|
||||
Scala bytecode and allowing virtual resolution to reach the server implementation.
|
||||
- All 158 plain-JVM tests and all 81 Java 8 Forge dedicated-server tests pass. Mod lifecycle dispatch is startup-only,
|
||||
so it is not a meaningful focused throughput or allocation target.
|
||||
- Characterized `MultipartEventHandler` against untouched Scala. Two plain-JVM cases freeze both singleton types, all
|
||||
twelve public event methods, priorities and the client-only highlight boundary; three Forge cases freeze companion
|
||||
registration on both buses, chunk load/unload cleanup, queued watches and END-phase tick dispatch.
|
||||
- Ported the singleton to a Java facade/companion pair with unchanged public names, descriptors and annotations. The
|
||||
proxy now names `MultipartEventHandler$.MODULE$` explicitly, preserving the exact object registered on both buses.
|
||||
- Server ticking still passes the configuration manager's live player list through Scala's Java-list buffer adapter;
|
||||
no copy or new traversal was introduced. All 160 plain-JVM and all 84 Java 8 Forge dedicated-server tests pass.
|
||||
@@ -1829,3 +1829,49 @@ exactly `MultipartMod.class` and `MultipartMod$.class`.
|
||||
startup phase, reaches server-started tests, and shuts down cleanly.
|
||||
- Raw `javap` comparison confirms identical public names/descriptors for `MultipartMod`, `MultipartMod$` and
|
||||
`MultipartPH`; reflection confirms identical FML annotations.
|
||||
|
||||
## 2026-08-30 — MultipartEventHandler Java port
|
||||
|
||||
The ForgeMultipart event handler is now an annotated Java facade/companion pair. The server proxy registers
|
||||
`MultipartEventHandler$.MODULE$` explicitly on both the FML and MinecraftForge event buses, preserving the same target
|
||||
identity as the Scala object expression.
|
||||
|
||||
### Observable behavior
|
||||
|
||||
No known runtime divergence. Chunk-data loading still runs at `HIGHEST` priority and delegates to
|
||||
`MultipartSaveLoad.loadTiles`. World unload still removes server packet state, clears the client tile cache for remote
|
||||
worlds, and otherwise clears the loading-world reference. Watch and unwatch events still update the two-stage chunk
|
||||
watcher state, and only the END server-tick phase flushes packets and promotes queued watches.
|
||||
|
||||
The highlight handler retains both `@SubscribeEvent` and `@SideOnly(CLIENT)`. Its null, block-hit and multipart-tile
|
||||
guards remain short-circuiting; a successful `BlockMultipart.drawHighlight` call still cancels the event. This client
|
||||
rendering path remains on the manual checklist because the automated harness is a dedicated server.
|
||||
|
||||
Server ticking still wraps the configuration manager's live `java.util.List` with Scala's `JavaConverters` buffer
|
||||
adapter before calling `MultipartSPH.onTickEnd`. This preserves the original `scala.collection.Seq` contract without
|
||||
copying the player list or adding a new traversal.
|
||||
|
||||
### ABI, annotations and source compatibility
|
||||
|
||||
`MultipartEventHandler` retains the exact six public static methods. `MultipartEventHandler$` retains `MODULE$` and
|
||||
the matching six public instance methods. Every method still carries `@SubscribeEvent`; only `tileEntityLoad` uses
|
||||
`HIGHEST`, the other five use `NORMAL`, none receive canceled events, and only `drawBlockHighlight` carries
|
||||
`@SideOnly(CLIENT)`.
|
||||
|
||||
The in-repo Scala registrations now spell the handler value as `MultipartEventHandler$.MODULE$`. The registered
|
||||
runtime identity is unchanged, and no frozen binary or audited source consumer names either handler class.
|
||||
|
||||
Accepted divergence: the Scala signature/marker attributes disappear, and the static Java facade gains only a private
|
||||
constructor. These do not change the runtime annotation data or callable public surface. The emitted class list remains
|
||||
exactly `MultipartEventHandler.class` and `MultipartEventHandler$.class`.
|
||||
|
||||
### Validation
|
||||
|
||||
- `MultipartEventHandlerCharacterizationTest`: 2 plain-JVM tests, unchanged from the Scala baseline.
|
||||
- `MultipartEventHandlerFunctionalTest`: 3 Forge tests, unchanged from the Scala baseline.
|
||||
- Complete plain-JVM suite: 160 tests, 0 failures, 0 errors.
|
||||
- Java 8 Forge dedicated-server suite: 84 tests, 0 failures, 0 errors.
|
||||
- The Forge run confirms the companion identity on both event buses and exercises load/unload, watch/unwatch and both
|
||||
server-tick phases before shutting down cleanly.
|
||||
- Raw `javap` comparison confirms identical public fields, method names and descriptors for both handler types;
|
||||
reflection confirms identical event annotations, and both jars emit exactly the same two handler classes.
|
||||
@@ -12,8 +12,8 @@ what breaks, and what is left. The other documents hold the reasoning.
|
||||
| `JAVA_MIGRATION_MANUAL_CHECKS.md` | What no automated test can cover, and must be checked by hand in a client |
|
||||
| `JAVA_MIGRATION_PROFILE.md` | The focused baseline, first measured result, findings, and exact rerun command |
|
||||
|
||||
Branch: `algent/java`. Base: `master`. 89 commits including separate characterization and port commits for
|
||||
`MultipartMod`.
|
||||
Branch: `algent/java`. Base: `master`. 91 commits including separate characterization and port commits for
|
||||
`MultipartEventHandler`.
|
||||
|
||||
## The one rule that matters
|
||||
|
||||
@@ -67,7 +67,7 @@ awk -F'"' '/<testsuite /{t+=$4;f+=$8;e+=$10} END{print "tests="t" failures="f" e
|
||||
grep -o 'tests="[0-9]*" skipped="[0-9]*" failures="[0-9]*" errors="[0-9]*"' run/server/junit-out/TEST-*.xml
|
||||
```
|
||||
|
||||
Current baseline: **158 plain-JVM tests, 81 Forge server tests, all passing at their last completed runs.**
|
||||
Current baseline: **160 plain-JVM tests, 84 Forge server tests, all passing at their last completed runs.**
|
||||
|
||||
### ABI diff against the reference
|
||||
|
||||
@@ -235,7 +235,8 @@ All eight load-bearing `$class` helpers from the inventory, both registries, and
|
||||
`MultiPartRegistry`, `TileMultipart`, `TMultiPart`, `TickScheduler`, `BlockMultipart`, the complete
|
||||
`IRedstonePart`/`RedstoneInteractions` unit, `MicroRecipe`, and the `TPartialOcclusionTile`, `TSlottedTile`, and
|
||||
`TRedstoneTile`, `TTileChangeTile`, `TFluidHandlerTile`, `TIInventoryTile`/`JInventoryTile`, `TileMultipartClient`, and
|
||||
`TRandomDisplayTickTile` Java-trait ports, plus `MultipartCompatiblity`/`MCPCCompatModule` and `MultipartMod`.
|
||||
`TRandomDisplayTickTile` Java-trait ports, plus `MultipartCompatiblity`/`MCPCCompatModule`, `MultipartMod`, and
|
||||
`MultipartEventHandler`.
|
||||
|
||||
Plus the six marker interfaces: `TSlottedPart`, `IRandomDisplayTick`, `INeighborTileChange`, `TRandomUpdateTick`,
|
||||
`ISidedHollowConnect`, `IMicroMaterialRender`, plus `MultipartHelper`, `TileCache`, `PacketScheduler` and the `ControlKeyModifer` pair.
|
||||
@@ -250,7 +251,7 @@ across the port. It is also where the two shim constraints in the gotchas list w
|
||||
`rayTraceAll`'s index production is now characterized, closing the gap the read-path cleanup left: the index written
|
||||
into `ExtendedMOP.data` is what `reduceMOP` hands back to every click, activate, harvest and pick block.
|
||||
|
||||
97 Java files, 36 Scala files, ~5,241 Scala lines left (non-blank; that is the metric this figure has always used).
|
||||
99 Java files, 35 Scala files, ~5,164 Scala lines left (non-blank; that is the metric this figure has always used).
|
||||
|
||||
## What is left, and in what order
|
||||
|
||||
@@ -342,6 +343,11 @@ methods and `MultipartMod$.MODULE$` retain their exact public descriptors and an
|
||||
as the mod instance, and `MultipartPH.channel()` still returns that same companion with its original descriptor. Two
|
||||
plain-JVM and two Forge tests freeze the shape, identity, completed lifecycle and server-stop cleanup.
|
||||
|
||||
`MultipartEventHandler` is now Java. Both singleton class names, all twelve public event methods, every event priority,
|
||||
and the client-only highlight boundary retain their exact shape. The proxy now names `MultipartEventHandler$.MODULE$`
|
||||
explicitly, and that same companion remains registered on both event buses. Two plain-JVM and three Forge tests freeze
|
||||
the ABI, annotations, bus identity, load/unload cleanup, chunk watches and END-phase tick dispatch.
|
||||
|
||||
**Medium.** The `handler` packages on both sides, `ItemMicroPart`,
|
||||
`MicroblockPlacement`, `PlacementGrids`, `BlockMicroMaterial`, `MissingMicroMaterial`, `ConfigContent`,
|
||||
`DefaultContent`.
|
||||
@@ -350,10 +356,10 @@ plain-JVM and two Forge tests freeze the shape, identity, completed lifecycle an
|
||||
narrow generator relaxation: Java-trait parent linearization, explicit field-accessor recognition, and exclusion of
|
||||
transient runtime caches from generated copying.
|
||||
|
||||
Take `multipart/handler/MultipartEventHandler.scala` next. Preserve both singleton types and event-bus identity; freeze
|
||||
every `@SubscribeEvent` priority plus the client-only highlight annotation before changing it. Exercise load/unload,
|
||||
chunk watch/unwatch and end-of-server-tick dispatch where the Forge harness can observe them, and keep the
|
||||
characterization and port commits separate.
|
||||
Take `microblock/handler/MicroblockMod.scala` next. Reuse the annotated facade/companion pattern, but also freeze the
|
||||
two `angelicaCompat` accessors alongside `MODULE$`, the exact `@Mod` metadata and all five handler annotations. Confirm
|
||||
that FML still uses the companion and completes the server lifecycle. Call `MicroblockProxy$.MODULE$` directly because
|
||||
its client overrides carry `@SideOnly`, and keep the characterization and port commits separate.
|
||||
|
||||
**Phase 6/7, last.** `Microblock` and the microblock shape hierarchy, `MicroblockGenerator`, `MultipartGenerator`, and
|
||||
all of `multipart/asm/`. The ASM subsystem should be last; freeze generated-class fixtures before touching it.
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package codechicken.multipart.handler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.MovingObjectPosition.MovingObjectType;
|
||||
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
|
||||
import net.minecraftforge.event.world.ChunkDataEvent;
|
||||
import net.minecraftforge.event.world.ChunkWatchEvent;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
import codechicken.multipart.BlockMultipart;
|
||||
import codechicken.multipart.TileCache;
|
||||
import codechicken.multipart.TileMultipart;
|
||||
import cpw.mods.fml.common.eventhandler.EventPriority;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import scala.collection.JavaConverters;
|
||||
|
||||
public final class MultipartEventHandler$ {
|
||||
|
||||
public static final MultipartEventHandler$ MODULE$ = new MultipartEventHandler$();
|
||||
|
||||
private MultipartEventHandler$() {}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public void tileEntityLoad(ChunkDataEvent.Load event) {
|
||||
MultipartSaveLoad.loadTiles(event.getChunk());
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void worldUnLoad(WorldEvent.Unload event) {
|
||||
MultipartSPH$.MODULE$.onWorldUnload(event.world);
|
||||
if (event.world.isRemote) {
|
||||
TileCache.clear();
|
||||
} else {
|
||||
MultipartSaveLoad.loadingWorld_$eq(null);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void chunkWatch(ChunkWatchEvent.Watch event) {
|
||||
MultipartSPH$.MODULE$.onChunkWatch(event.player, event.chunk);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void chunkUnWatch(ChunkWatchEvent.UnWatch event) {
|
||||
MultipartSPH$.MODULE$.onChunkUnWatch(event.player, event.chunk);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void drawBlockHighlight(DrawBlockHighlightEvent event) {
|
||||
if (event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK
|
||||
&& event.player.worldObj.getTileEntity(
|
||||
event.target.blockX,
|
||||
event.target.blockY,
|
||||
event.target.blockZ) instanceof TileMultipart
|
||||
&& BlockMultipart
|
||||
.drawHighlight(event.player.worldObj, event.player, event.target, event.partialTicks)) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SuppressWarnings("unchecked")
|
||||
public void serverTick(TickEvent.ServerTickEvent event) {
|
||||
if (event.phase == TickEvent.Phase.END) {
|
||||
List<EntityPlayerMP> players = MinecraftServer.getServer().getConfigurationManager().playerEntityList;
|
||||
MultipartSPH$.MODULE$.onTickEnd(JavaConverters.asScalaBufferConverter(players).asScala());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package codechicken.multipart.handler;
|
||||
|
||||
import net.minecraftforge.client.event.DrawBlockHighlightEvent;
|
||||
import net.minecraftforge.event.world.ChunkDataEvent;
|
||||
import net.minecraftforge.event.world.ChunkWatchEvent;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
import cpw.mods.fml.common.eventhandler.EventPriority;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.common.gameevent.TickEvent;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public final class MultipartEventHandler {
|
||||
|
||||
private MultipartEventHandler() {}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
public static void tileEntityLoad(ChunkDataEvent.Load event) {
|
||||
MultipartEventHandler$.MODULE$.tileEntityLoad(event);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void worldUnLoad(WorldEvent.Unload event) {
|
||||
MultipartEventHandler$.MODULE$.worldUnLoad(event);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void chunkWatch(ChunkWatchEvent.Watch event) {
|
||||
MultipartEventHandler$.MODULE$.chunkWatch(event);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void chunkUnWatch(ChunkWatchEvent.UnWatch event) {
|
||||
MultipartEventHandler$.MODULE$.chunkUnWatch(event);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
public static void drawBlockHighlight(DrawBlockHighlightEvent event) {
|
||||
MultipartEventHandler$.MODULE$.drawBlockHighlight(event);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void serverTick(TickEvent.ServerTickEvent event) {
|
||||
MultipartEventHandler$.MODULE$.serverTick(event);
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package codechicken.multipart.handler
|
||||
|
||||
import codechicken.multipart.{
|
||||
TileCache,
|
||||
TileMultipart,
|
||||
MultiPartRegistry,
|
||||
BlockMultipart
|
||||
}
|
||||
import cpw.mods.fml.common.eventhandler.{EventPriority, SubscribeEvent}
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import codechicken.lib.packet.PacketCustom
|
||||
import net.minecraftforge.event.world._
|
||||
import java.util.EnumSet
|
||||
import scala.collection.JavaConverters._
|
||||
import java.util.List
|
||||
import net.minecraft.entity.player.EntityPlayerMP
|
||||
import cpw.mods.fml.relauncher.SideOnly
|
||||
import cpw.mods.fml.relauncher.Side
|
||||
import net.minecraftforge.client.event.DrawBlockHighlightEvent
|
||||
import net.minecraft.util.MovingObjectPosition
|
||||
import net.minecraft.util.MovingObjectPosition.MovingObjectType
|
||||
import cpw.mods.fml.common.gameevent.TickEvent
|
||||
|
||||
object MultipartEventHandler {
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
def tileEntityLoad(event: ChunkDataEvent.Load) {
|
||||
MultipartSaveLoad.loadTiles(event.getChunk)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
def worldUnLoad(event: WorldEvent.Unload) {
|
||||
MultipartSPH.onWorldUnload(event.world)
|
||||
if (event.world.isRemote) {
|
||||
TileCache.clear()
|
||||
} else {
|
||||
MultipartSaveLoad.loadingWorld = null
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
def chunkWatch(event: ChunkWatchEvent.Watch) {
|
||||
MultipartSPH.onChunkWatch(event.player, event.chunk)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
def chunkUnWatch(event: ChunkWatchEvent.UnWatch) {
|
||||
MultipartSPH.onChunkUnWatch(event.player, event.chunk)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
def drawBlockHighlight(event: DrawBlockHighlightEvent) {
|
||||
if (
|
||||
event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK &&
|
||||
event.player.worldObj
|
||||
.getTileEntity(
|
||||
event.target.blockX,
|
||||
event.target.blockY,
|
||||
event.target.blockZ
|
||||
)
|
||||
.isInstanceOf[TileMultipart]
|
||||
) {
|
||||
if (
|
||||
BlockMultipart.drawHighlight(
|
||||
event.player.worldObj,
|
||||
event.player,
|
||||
event.target,
|
||||
event.partialTicks
|
||||
)
|
||||
)
|
||||
event.setCanceled(true)
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
def serverTick(event: TickEvent.ServerTickEvent) {
|
||||
if (event.phase == TickEvent.Phase.END)
|
||||
MultipartSPH.onTickEnd(
|
||||
MinecraftServer.getServer.getConfigurationManager.playerEntityList
|
||||
.asInstanceOf[List[EntityPlayerMP]]
|
||||
.asScala
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -78,8 +78,8 @@ class MultipartProxy_serverImpl {
|
||||
def init() {}
|
||||
|
||||
def postInit() {
|
||||
FMLCommonHandler.instance().bus().register(MultipartEventHandler)
|
||||
MinecraftForge.EVENT_BUS.register(MultipartEventHandler)
|
||||
FMLCommonHandler.instance().bus().register(MultipartEventHandler$.MODULE$)
|
||||
MinecraftForge.EVENT_BUS.register(MultipartEventHandler$.MODULE$)
|
||||
PacketCustom.assignHandler(MultipartSPH.channel, MultipartSPH)
|
||||
PacketCustom.assignHandshakeHandler(
|
||||
MultipartSPH.registryChannel,
|
||||
|
||||
Reference in new issue
Block a user