In recent versions of Wuthering Waves, especially after the major 3.0 and 3.1 updates, I keep seeing the same complaints across many communities:

  • “The game has terrible memory optimization.”
  • “Why does it take 120GB on mobile just to update?”
  • “The devs say they listen to feedback, but the storage footprint keeps growing.”

As someone with a Computer Science background and also someone who enjoys Wuthering Waves quite a lot, I decided to dig deeper into this issue.

This post is not meant to blindly defend Kuro Games. Instead, the goal is to explain why the memory situation is much more complicated than “the devs didn’t optimize the game.”

Many of the problems come from a combination of:

  • Unreal Engine 4 architectural limitations
  • early development decisions
  • and a growing amount of technical debt from a live-service project

If the technical parts feel too dense, feel free to skip to the TL;DR at the end.


1. The Hard Reference Problem in UE4

Unreal Engine documentation explains asset management like this:

“A primary asset is an asset that can be manipulated directly by the asset manager, while secondary assets are assets that are loaded automatically when a primary asset references them. Primary assets are the type referenced in the cooking and chunking process.”

Source: https://dev.epicgames.com/documentation/en-us/unreal-engine/cooking-and-chunking

In simple terms:

Maps such as Jinzhou or Rinascita are treated as Primary Assets, and they are packed into separate chunks during the build process.

If both maps hard reference the same asset (e.g., TreeA), the engine can end up duplicating it during the cooking process depending on how shared content is packaged.

The build output can look like this:

Map A (Chunk 10)
  - TreeA texture

Map B (Chunk 11)
  - TreeA texture (duplicate)

Result:
Multiple compressed copies inside .pak files

This duplication behavior is normal in UE4’s chunking system. The small example above might look harmless, but at the scale of a large open-world game, you can imagine what happens when thousands of textures and meshes are referenced across multiple maps.

Why not just put everything into one chunk?

One possible solution would be to place all shared assets into Chunk 0, the default chunk.

However, this introduces another trade-off. If Chunk 0 becomes too large:

  • initial load times increase dramatically
  • memory pressure increases because the engine must load a massive chunk at startup

So developers face a fundamental trade-off: smaller storage footprint vs runtime performance and loading speed

Evidence from Datamined Asset Configurations

Datamined configuration files strongly suggest that Wuthering Waves organizes assets and regions using a sub-package system built around PakName identifiers.

While we do not currently have access to a full AssetRegistry or chunk manifest, these structures indicate that content is mounted and referenced through discrete pak bundles. In such a system, hard references across pak boundaries can introduce structural risks: either additional packs must be mounted at runtime, or assets may be duplicated during the cooking process depending on the project’s packaging strategy.

Here is the supporting evidence:

Evidence 1 - Regional Pak Bundles

One configuration file maps world regions to specific pak bundles:

Source: https://github.com/Arikatsu/WutheringWaves_Data/blob/main/BinData/DownLoad/mapblockinfo.json

{
  "BlockId": 2,
  "MapId": 900,
  "PakName": "Pack_HHA_Underground",
  "RegionName": "HHA_Underground"
}

Explanation: This suggests that the game mounts pak bundles at a regional level, with different world blocks associated with specific pack identifiers. In practice, this resembles a sub-package system where different parts of the world can be streamed or downloaded independently.

Evidence 2 - Gameplay Systems Referencing Pak Bundles

Another configuration file links quest data directly to pak identifiers:

Source: https://github.com/Arikatsu/WutheringWaves_Data/blob/main/BinData/QuestRefVideo/questrefvideoconfig.json

{
  "QuestId": 114000020,
  "PakName": "114_2"
}

Explanation: Here, gameplay logic references assets through a PakName identifier. This indicates that packaged resources are exposed as first-class data references inside the game’s content pipeline.

Evidence 3 - Packaging Rules and Map Paths

A third configuration file shows map paths and packaging rules:

Source: https://github.com/Arikatsu/WutheringWaves_Data/blob/main/BinData/instance_dungeon/akimapsource.json

{
  "MapId": 32,
  "PakRule": 0,
  "MapPath": "/Game/Aki/UniverseEditor/Test/QuestTest"
}

Explanation: The presence of fields such as PakRule suggests that packaging logic exists at the configuration level. Interestingly, some map paths still point to test or QA environments. This does not necessarily mean those maps are included in the shipping build, but it illustrates how asset indexing can include development paths unless the cook pipeline explicitly excludes them.

Taken together, these structures strongly suggest that the content pipeline is organized around pak-based distribution units. In such architectures, hard references across pack boundaries can lead to asset duplication or force additional pak mounts depending on the packaging strategy, unless the project uses carefully designed shared chunks.

Limitations of this analysis

Because the actual game project is not publicly available, this analysis relies on datamined configuration files rather than the full Unreal Engine asset registry.

Without access to the complete AssetRegistry.bin or chunk manifests, it is not possible to definitively prove asset duplication across pak bundles. However, the configuration structures observed here are consistent with the packaging patterns documented in Unreal Engine’s cooking and chunking pipeline.

Note: Since version 2.8, community reverse-engineering and datamined repository changes have suggested that Kuro has begun introducing parallel C#-based content paths - for example, directories such as level_entity_csharp and prefab_csharp appearing alongside older structures. If this interpretation is correct, it would fit a broader effort to gradually reduce legacy runtime overhead and make deeper architectural cleanup more feasible over time.

2. Dependency Hell

Another common question is:

Why can’t Kuro just delete old events and quest content to save space?

To understand this, imagine playing Jenga. Old events and legacy quest systems are like blocks near the bottom of the tower. If you remove one block, you risk collapsing everything above it.

In software architecture, this situation is often called dependency hell. Many systems depend on the same shared databases or configuration files.

Example: the MoonChasing event (1.2 Event)

After examining datamined files from version 3.1:

https://github.com/Arikatsu/WutheringWaves_Data/tree/3.1

The MoonChasing event shows how tightly integrated older content can be.

Inside aki_base.csv:

  • TrackMoonActivity
  • TrackMoonActivityReward
  • TrackMoonPhaseActivity

These entries exist inside a shared database called db_activity. This database is used by many other systems in the game. Only a small portion of MoonChasing data exists inside a dedicated database (db_MoonChasing.db). This means the event cannot simply be deleted without potentially breaking other features.

Another example appears in commonparam.json. There are several MoonChasing keys embedded among thousands of unrelated parameters such as:

  • TowerRecordMaxLevel
  • MoveCheckSpeed

Some of these entries also reference the Sequence system, which is part of Unreal Engine’s shared cinematic pipeline. Removing those entries safely would require verifying that no part of the codebase references them anymore, which is extremely difficult in a constantly evolving live-service game.

And even if this cleanup were possible, it would likely not reduce the game size significantly, because the biggest storage cost still comes from the asset architecture discussed above.

3. Predownload and Update Size in UE4

Unreal Engine uses a patching system that can be somewhat storage-heavy.

From Epic’s own documentation:

“This method can eventually use a lot of data storage, because this method keeps all old files, and merely points the game to the newest files that exist.”

Source: https://dev.epicgames.com/documentation/en-us/unreal-engine/how-to-create-a-patch-platform-agnostic

How predownload works in practice

Imagine the following situation:

  • Version 3.0 on your phone: ~55GB
  • Version 3.1 on the server: ~60GB

During predownload, the client downloads the entire new build into a temporary directory.

So for a short period, your device stores both: 55GB (old version) + 60GB (new version) = ~115GB total

The old build is kept mounted until the update is verified and ready to swap. Once verification is complete, the system replaces the old version and deletes it. This is why predownloads can temporarily require almost double the final storage size.

A Lego analogy

A simple analogy is building a Lego city.

After finishing the city, you glue everything together and put it inside a sealed box (.pak file). Now imagine you want to add a new building. You cannot easily modify the existing glued model without breaking it.

Instead, you must:

  • rebuild the entire city with the new building included
  • seal the new version
  • discard the old one

This is essentially how large .pak updates work.

Why not use smaller modular files?

UE4 does support more modular asset structures. However, there are trade-offs.

If the game splits assets into hundreds or thousands of tiny files, the system must manage:

  • far more file handles
  • higher I/O overhead
  • increased disk fragmentation

For an engine like UE4 that already struggles with CPU bottlenecks in large cities, increasing I/O complexity can make runtime performance worse. This is especially problematic on mobile devices with slower storage.

Additionally, Wuthering Waves also rotates AES encryption keys, which adds another layer of complexity to patching and asset management.

Unity’s Approach: AssetBundles

Unity uses a very different system.

From the Unity documentation:

“An AssetBundle is an archive file that you can use to group together assets to create downloadable content (DLC), or reduce the initial installation size of your application.”

Source: https://docs.unity3d.com/Manual/AssetBundlesIntro.html

With AssetBundles and Addressables, Unity organizes assets in a modular structure. Instead of rebuilding the entire city when adding a house, Unity can simply download a new module and replace the old one. This allows more flexible patching and easier cache cleanup.

However, this modular approach also introduces runtime complexity that Unreal historically handled differently.

A Note on Compression Reality

Some community analysis has shown that WuWa’s .pak files are already highly compressed.

One Reddit post estimated the average entropy of the game’s .pak data at around 7.6 bits per byte, which is very close to the theoretical compression limit of 8 bits per byte. For context, completely random data has an entropy of 8 bits per byte, which represents the theoretical limit for lossless compression. In information theory, this means the data is already highly randomized and there is very little redundant information left to compress.

Source: https://www.reddit.com/r/WutheringWaves/comments/1rtfrlg/lets_talk_about_storage_compression_and_how_well/

In other words, the problem is likely not poor compression algorithms.

The key point is that compression efficiency and asset architecture are separate problems. Even perfectly compressed pak files can still produce a large install size if the underlying asset graph contains duplication or tightly coupled dependencies.

4. Conclusion

It is very easy for players to look at a 120GB update on their phones and conclude that the developers are simply lazy or ignoring feedback. However, the reality of software engineering is often much harsher.

The engineering team at Kuro Games is quietly undertaking a massive, high-risk architectural overhaul while simultaneously keeping a global live-service game running. Migrating gameplay systems away from TypeScript and restructuring deeply rooted legacy code is an incredibly expensive and thankless process - when they do it perfectly, players won’t notice anything new, but if they make a single mistake, the game crashes.

These are silent efforts. They are paying off years of technical debt so the game can survive in the long run. As players, while the storage bloat is undeniably frustrating, I hope we can also recognize and appreciate the monumental engineering challenges they are tackling behind the scenes.

With the direction they seem to be taking, I personally remain cautiously optimistic.

A small architectural note

Besides asset pipeline and patching constraints, Wuthering Waves likely also has additional architectural limitations at the runtime layer, such as scripting systems that may affect performance.

However, that is a separate and much deeper topic. I will leave a detailed discussion of runtime architecture and GameThread constraints for a future post.

TL;DR

Wuthering Waves is large because of a combination of:

  • UE4 hard reference risks causing asset duplication across chunks
  • dependency chains from older live-service content
  • and a patching pipeline that temporarily stores multiple versions of the game during updates

The issue is not compression; it is architecture. Kuro is likely trying to balance runtime performance, stability, and storage usage, which often forces difficult trade-offs in large Unreal Engine projects. But behind the scenes, their engineering team is quietly making massive architectural shifts to pay off technical debt and secure the game’s future.