Links
Tags
Creators
Details
(DEPRECATED)1.4.15-1.20.1-fabric
Compatibility
Changes
Changelog
[1.4.16-1.20.1-fabric] - 2026-05-09
Security & Critical Bug Fixes
-
Fixed UUID mismatch in multiplayer screen texture cleanup (
ClientMod.java)S2C_STOP_SCREENandS2C_SCREENwere usingmcc.player.getUuid()forremove()instead ofpcOwner.- This caused screen textures from other players to never be destroyed, leading to a severe memory leak (
NativeImage,NativeImageBackedTexture,Identifier). - Also fixed a double-close bug where
vmScreenTextureNI.close()was called instead ofvmScreenTextureNIBT.close()inS2C_SCREEN.
-
Fixed component loss when placing last PC case from stack (
ItemPCCase.java,ItemPCCaseSidepanel.java)decrement(1)was called before reading NBT data. Whencount == 1, the stack becameEMPTYandgetNbt()returnednull, causing permanent loss of all PC components (motherboard, CPU, RAM, HDD, ISO).- Now NBT is saved to a local variable before decrementing.
-
Fixed server crash on missing inventory item (
MainMod.java)removeStck()threwRuntimeExceptionwhen an item was not found, crashing the entire server.- Replaced with logging via
LOGGER.error()and graceful return. - Also hardened against desync between
contains()check andremoveStck()execution.
-
Fixed ClassCastException exploit in C2S_ORDER (
MainMod.java)- No validation that
readItemStack().getItem()is actually anOrderableItem. A malicious client could send any item, causing a server crash. - Added
instanceof OrderableItemcheck with logging of invalid packets. - Same fix applied to
S2C_SYNC_ORDERhandler inClientMod.java.
- No validation that
-
Fixed IllegalArgumentException in C2S_ADD_CPU / C2S_ADD_RAM (
MainMod.java)- When
dividedByormbvalues did not match expected constants,lookingForremainednull, causingnew ItemStack(null)→ server crash. - Added
lookingFor != nullguard before creatingItemStack.
- When
-
Fixed HDD removal ignoring NBT (
MainMod.java)removeStck()matched only byItemtype, not NBT. If a player had multiple HDDs with different VHD files, the wrong disk could be removed.- Enhanced
removeStck()to prefer exact NBT match before falling back to type-only match.
-
Fixed VM always created with only 1 CPU (
GuiPCEditing.java)Math.min(1, ...)in the "create new VM" branch always returned1, regardless of host CPU count or player settings. The "modify existing" branch already used correctMath.max(1, ...).- Fixed to
Math.max(1, ...)— VM now gets the correct number of CPUs.
-
Fixed ConcurrentModificationException in ServerMixin (
ServerMixin.java)MainMod.orders.remove()was called duringfor-eachiteration overHashMap.values(), guaranteeing a crash every time an order completed.- Implemented deferred removal: collect keys to remove in a list, then delete after the loop.
-
Fixed NPE when sending packets to disconnected players (
ServerMixin.java)playerManager.getPlayer()could returnnullif a player logged out. Calling.getWorld()orServerPlayNetworking.send(null, ...)caused NPE.- Added null checks before all
getPlayer()usages.
-
Fixed deadlock in insertISO (
GuiPCEditing.java)wait()withoutwhileloop and without timeout — ifnotify()was called beforewait(), the thread hung forever.- Replaced with
while (condition) { lock.wait(5000); }pattern using a sharedClientMod.VM_TURNING_ON_LOCK. - Also fixed the case where a new
GuiPCEditinginstance had a different monitor object than the onenotify()was called on.
-
Fixed busy-wait burning 100% CPU in turnOffPC (
GuiPCEditing.java)while(ClientMod.vmTurningOn) {}was an infinite spin-loop.- Replaced with
Thread.sleep(50)inside the loop. Also addedvolatiletovmTurningOnso the thread actually sees state changes.
-
Fixed Timer thread leak in GuiFocus (
GuiFocus.java)- A new
Timerwas created every timeGuiFocusopened, butcancel()was only called onNullPointerException(singleplayer). In multiplayer, timers leaked indefinitely. - Timer is now stored as a field and cancelled in
removed().
- A new
-
Fixed NPE-based control flow in singleplayer (
GuiFocus.java)getCurrentServerEntry().addressthrows NPE in singleplayer. The code usedcatch(NullPointerException)as control flow.- Replaced with explicit
== nullcheck.
-
Fixed resource leak in VBoxManage (
VBoxManage.java)BufferedReaderandInputStreamwere not closed infinally, leaking file descriptors on exceptions.- Wrapped in
try-with-resources.
-
Fixed FileWriter not closed on exception (
ClientMod.java)increaseVHDNum()opened aFileWriterwithouttry-with-resourcesorfinally. On any exception, the file handle leaked.- Wrapped in
try-with-resources.
-
Fixed Unpooled.buffer() leaks across all packet handlers
new PacketByteBuf(Unpooled.buffer())was used inClientMod.java,GuiPCEditing.java,ServerMixin.java, andPlayerManagerMixin.javainstead ofPacketByteBufs.create().- Replaced all instances with the proper Fabric API method.
-
Fixed regex bug in hard drive selection (
GuiCreateHarddrive.java)split(" | ")treated" | "as a regex (space OR space), effectively equivalent tosplit(" ").- Any VHD filename containing a space was broken. Replaced with
indexOf(" | ")+substring()for literal matching.
-
Fixed iron ingot overpayment loss (
EntityDeliveryChest.java)- When a player overpaid,
is.increment()was called on an already-emptyItemStack(afterdecrement()), causing the excess ingots to be permanently lost. - Fixed by saving the count before
decrement(), then creating a newItemStackfor the refund and usingplayer.getInventory().offerOrDrop().
- When a player overpaid,
-
Fixed matrix stack imbalance on exception (
HeldItemMixin.java)matrices.push()in HEAD andmatrices.pop()in TAIL were not atomic. An exception between them left the stack unbalanced.- Wrapped rendering in
try-finallyto guaranteematrices.pop().
-
Fixed NBT key inconsistency (
EntityPC.java,ItemPCCase.java,ItemPCCaseSidepanel.java)- Constructor used different NBT keys (
x64,MoboInstalled,GPUInstalled,RAMSlot0,VHDName,ISOName) thanreadCustomDataFromNbt/writeCustomDataToNbt(X64,MotherboardInstalled,GpuInstalled,GbRamSlot0,HardDriveFileName,IsoFileName). - This worked only because the two paths never crossed, but was a refactoring minefield.
- Unified all keys to match
writeCustomDataToNbtformat. Added backward compatibility in constructor to read both old and new keys.
- Constructor used different NBT keys (
New Features
-
Transition to VBoxManage CLI (VBox CLI)
- Replaced the deprecated VirtualBox JWS (Java Web Services) API with a new
VBoxManage.javawrapper that calls theVBoxManagecommand-line tool directly. - The JWS API was removed in VirtualBox 7.0+, making the old mod completely incompatible with modern VirtualBox installations.
- All VM operations are now performed via CLI:
createVm,modifyVm,startVm,powerOffVmstorageAttach,addStorageController,removeStorageControllermountMedium,unmountMediumputScancodes,putMouseEventtakeScreenshotgetVmState,vmExists,discardSavedState
- Added
executeSilent()for optional operations that may fail on newer VBox versions (e.g.,--accelerate2dvideoin VBox 7.x). - Added connection test (
testConnection()) and version detection (getVersion()).
- Replaced the deprecated VirtualBox JWS (Java Web Services) API with a new
-
Support for VirtualBox 7.x and newer
- VBox 7.0+ removed
vboxwebsrv(the SOAP web service). The mod now works with any VirtualBox version that providesVBoxManage. - Added parsing of
showvminfo --machinereadableoutput for medium attachment state, compatible with VBox 7.x output format. --accelerate2dvideois now handled silently (ignored if unsupported) instead of crashing.
- VBox 7.0+ removed
Performance Improvements
-
Added
volatileto all cross-thread shared state (ClientMod.java)vmTurnedOn,vmTurningOn,vmTurningOff,vmUpdateThread,vmTextureBytes,vmTextureBytesSize.- Prevents infinite busy-waits and race conditions between the render thread and VM update thread.
-
Added
synchronizedto texture byte transfers (VMRunnable.java+ClientMod.java)vmTextureBytes/vmTextureBytesSizewere updated in one thread and read in another without any synchronization, risking torn reads.- Added
VM_TEXTURE_LOCKwith propersynchronizedblocks on both write (VM thread) and read (render thread) sides.
-
Added
synchronizedto tablet texture streaming (TabletOS.java)byteArrayInputStreamwas written by the tablet renderer thread and read by the main render thread without synchronization.- Added
TEXTURE_SYNClock with local copy pattern.
-
Throttled
isMediumEjected()polling (GuiPCEditing.java)- Previously called
VBoxManage showvminfoevery frame (~60 times/second), spawning a new OS process each time. - Now checks every 2 seconds, reducing CPU and disk load by ~99%.
- Previously called
-
Throttled tablet renderer (
GameloopMixin.java)- Tablet renderer
while(true)loop had noThread.sleep(), pinning a CPU core at 100%. - Added
Thread.sleep(33)for ~30 FPS cap. - Also replaced silent
ConcurrentModificationExceptionswallowing with proper logging.
- Tablet renderer
-
Reduced S2C_SYNC_ORDER network spam (
ServerMixin.java)- Order sync packet was sent 20 times/second per order regardless of whether status changed.
- Now tracks
lastSyncedStatusand only sends the packet when the status actually changes.
Architecture & Code Quality
-
Proper use of
PacketByteBufs.create()throughout- Eliminates manual buffer lifecycle management and potential leaks.
-
Added SLF4J/Log4j logging (
MainMod.java)- Replaced
RuntimeExceptionserver crashes withLOGGER.error()for inventory desync events.
- Replaced
-
Backward compatibility for old NBT saves
- Old PC cases placed in the world before this update will still load correctly thanks to dual-key reading in
EntityPCconstructor.
- Old PC cases placed in the world before this update will still load correctly thanks to dual-key reading in
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:l3oi8QvT:qo9Cc3xQ"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:l3oi8QvT:qo9Cc3xQ"
}

