Introduction
When you build games with Unity, you eventually run into the problem of managing static game data—often called master data in Japanese game development.
At first, ScriptableObject may be more than enough. If your project has a few dozen items, a few dozen enemies, and only a small number of stage definitions, ScriptableObject is convenient because you can inspect and edit everything directly in the Unity Editor.
As the project grows, however, the situation changes.
You may end up with tables for items, characters, skills, quests, rewards, shops, gacha pools, stages, enemy placements, progression curves, and localization text. The data is no longer edited only by programmers. Planners and game designers may need to work with it in Excel or Google Sheets.
At that point, the problem is no longer just choosing a file format. You need to think about questions such as:
- How do you load a large amount of data quickly?
- How do you write ID lookups and composite-key queries safely?
- Should CSV or JSON be parsed directly at runtime?
- Is it reasonable to create a large number of Dictionaries?
- How do you validate references between tables?
- How do you debug data after converting it to binary?
- How do you connect the source data edited by planners to the data loaded by Unity?
For the runtime loading and lookup part of that problem, one strong option is Cysharp's MasterMemory.
The official README describes MasterMemory as a “Source Generator based Embedded Typed Readonly In-Memory Document Database” for .NET and Unity. In practical terms, you define your schema as C# types, a Source Generator creates a typed read-only in-memory database API, and the application loads MessagePack binary data that can be queried through type-safe methods.
The official README highlights performance compared with SQLite, low allocation during queries, a small database size, and generated database structures that are type-safe and IDE-friendly.
Cygames Engineers' Blog also has useful articles about the design philosophy behind MasterMemory and the Validator introduced in v2. These articles are in Japanese, but they are valuable references:
- MasterMemory: A Read-Only In-Memory Database for Unity and .NET Core
- MasterMemory v2 for Parameter Validation in Master Data
There are also clear Japanese articles covering Unity setup and basic usage:
- Introducing MasterMemory for Master Data Management in Unity
- Learning Efficient Game Master Data Management from MasterMemory
One thing to keep in mind is that information about MasterMemory v2 and earlier often assumes the old Code Generator workflow, while v3 and later are based on Source Generators. Always check the README and release notes for the version you actually plan to use.
Unless otherwise noted, the API behavior described in this article refers to MasterMemory v3.0.4.
This article is not a step-by-step installation tutorial. Instead, it focuses on what MasterMemory solves in a production Unity project and where its responsibilities end.
The conclusion is straightforward: MasterMemory is a strong option when you need to handle large amounts of read-only static game data with fast loading, low memory overhead, and type-safe queries. It does not, however, decide how planners edit the source data, how localization is managed, how diffs are reviewed, how CI generates the database, or how environment-specific data is deployed.
The most useful way to evaluate it is as a fast, type-safe, read-only database, separate from the pipeline around it.
The Basic Role of MasterMemory
Three characteristics are central to understanding MasterMemory:
- Read-only
- In-memory
- Strongly typed
That combination is a good fit for static game data.
Most game master data is not supposed to be mutated continuously during gameplay.
A live-service game may download a new data package from a server or switch to a different dataset for an event. But once the client has loaded a dataset, it is usually treated as immutable for the rest of the session.
If item definitions or skill definitions can be modified directly during gameplay, several problems become more likely:
- It becomes difficult to know when a value changed.
- It becomes difficult to identify which system changed it.
- Cached data can become inconsistent.
- Threading and asynchronous processing become harder to reason about.
- Bugs become less reproducible.
Treating master data as immutable after loading simplifies the runtime architecture.
MasterMemory is designed around that assumption.
“Read-only” does not mean that a running game can never switch to new master data. It means that the active MemoryDatabase should be treated as an immutable snapshot. When new data is needed, you can construct a new database from a new binary package, or use ToImmutableBuilder() to apply changes and build another immutable database, then replace the shared reference.
The data is converted to binary ahead of time, loaded into a MemoryDatabase at startup or another appropriate point, and queried through generated table APIs.
var db = new MemoryDatabase(binary);
var item = db.ItemMasterTable.FindByItemId(1001);
The examples in this article use FindBy..., but production code must also account for missing keys.
In the v3.0.4 README, some older wording still appears in one place, but the MemoryDatabase / RangeView section and the Generator Option documentation describe the current behavior: for a unique key, FindBy... throws KeyNotFoundException when the key is missing. A generated TryFindBy... method is also available.
For IDs originating outside the validated master-data package—such as save data, server responses, or QA commands—using TryFindBy... is usually the safest default.
If the Generator Option IsReturnNullIfKeyNotFound is enabled, unique-key lookups can return null instead. A FindBy... query on a NonUnique key returns a RangeView<T>, and an unmatched query produces an empty result.
A useful division of responsibility is:
- Detect invalid references inside the master-data package with Validator.
- Defend against invalid external IDs at runtime.
Once this structure is in place, runtime access becomes much simpler.
You no longer need to parse CSV at startup. You can reduce the amount of JSON-to-Dictionary conversion code. You also need fewer hand-written lookup Dictionaries for every table.
ScriptableObject, SQLite, or JSON loaded through Addressables may still be the right choice in some projects. But for a large set of read-heavy tabular data, MasterMemory's design maps well to common game requirements.
What MasterMemory Solves
MasterMemory is particularly effective in the following areas:
- Storing master data in a read-only in-memory database
- Generating type-safe query APIs with a Source Generator
- Supporting PrimaryKey and SecondaryKey lookups
- Supporting composite keys, range queries, and closest-value queries
- Loading MessagePack binary data
- Interning repeated strings to share references
- Validating data consistency through Validator
- Exposing Metadata that can support surrounding tools
Existing articles often focus on setup, raw performance, or comparisons with Dictionary.
The official README includes claims such as 4700 times faster than SQLite, zero allocation per query, and DB size is small. Those numbers come from specific benchmark conditions and example datasets. The actual difference in your project depends on the schema, record count, query frequency, load timing, and target platform.
In Unity, perceived performance can also change depending on:
- Editor versus device
- Mono versus IL2CPP
- Whether the binary is loaded from Resources, StreamingAssets, Addressables, an AssetBundle, or a remote server
- The I/O cost of retrieving the binary
- Whether the caller applies LINQ or copies results into another collection
zero allocation per query should be understood as a property of the query API itself. If the calling code adds LINQ chains or copies the result into a new List, those operations may allocate separately.
Even with those caveats, the underlying direction is well suited to static game data: build sorted structures ahead of use, rely on binary search, and avoid unnecessary runtime work.
The Cygames article explains that MasterMemory uses sorted arrays and binary search to achieve lookup performance close to Dictionary while improving construction cost and memory efficiency in suitable workloads.
The following sections focus on the production benefits that become important beyond a simple benchmark.
Read-Only Is a Strength, Not Just a Restriction
MasterMemory is read-only.
That can sound like a limitation, but for game master data it is often a major advantage. Master data usually describes definitions used to interpret the mutable state of the game.
For example, the number of items owned by a player belongs to save data and changes over time.
The definition that item ID 1001 is a herb with a price of 50 and a healing value of 100 is master data. That definition should normally remain stable while the game is running.
Mixing those two categories makes the architecture harder to maintain.
// Mutable state
playerInventory.SetCount(itemId, count);
// Immutable definition
var item = master.ItemMasterTable.FindByItemId(itemId);
Treating master data as read-only provides several benefits:
- Better reproducibility
- Less rebuilding of lookup caches after data changes
- Simpler code on the consumer side
- More stable behavior after asynchronous initialization
- Easier tests with deterministic inputs
- Protection against accidental runtime mutation
The same master data may be consumed by battle logic, UI, shops, rewards, tutorials, localization, and visual presentation. If any of those systems can mutate the shared definitions, assumptions made by the other systems can collapse.
Read-only data narrows runtime responsibilities and makes those shared assumptions safer.
Repeated Strings Can Be Shared Automatically
By default, MasterMemory interns identical strings while constructing the database.
Static game data often repeats category names, localization keys, asset identifiers, and other strings across many rows. Without interning, identical text may exist as multiple string instances. With interning, those rows can share the same reference.
This can be useful for denormalized tables where strings are repeated to make queries or exports simpler. The behavior can be disabled through the internString argument of MemoryDatabase, so it can be evaluated against the actual characteristics of the data and measured on target devices.
It is less visible than query speed, but for a game that keeps large tables resident in memory, it is a practical benefit.
Type-Safe Query APIs Improve Maintainability
One of MasterMemory's strongest features is the type-safe query API generated by its Source Generator.
The official README explains that the C# schema produces a typed database structure with IDE completion.
For a small project, loading CSV into a Dictionary may be enough.
var value = row["ItemId"];
As a project grows, string-based access becomes fragile:
- Column-name typos are not detected until runtime.
- Renaming a column is difficult to propagate safely.
- Refactoring support is weak.
- Type conversion logic spreads across the codebase.
- It becomes unclear which columns are valid lookup keys.
With MasterMemory, PrimaryKey and SecondaryKey attributes define the indexes, and query methods are generated from the schema.
The following is a conceptual example. Follow the recommended definitions for the versions of MasterMemory and MessagePack used by your project.
[MemoryTable("item"), MessagePackObject(true)]
public sealed class ItemMaster
{
[PrimaryKey]
public int ItemId { get; init; }
[SecondaryKey(0), NonUnique]
public ItemType Type { get; init; }
public string NameKey { get; init; }
public int Price { get; init; }
}
In this article, the generated table API for ItemMaster is consistently written as ItemMasterTable. The string in [MemoryTable("item")] identifies the table in the database binary; it serves a different purpose from the generated C# type and property names.
var item = db.ItemMasterTable.FindByItemId(itemId);
var weapons = db.ItemMasterTable.FindByType(ItemType.Weapon);
The benefit is not merely shorter code.
The available lookup paths become explicit in the API. IDE completion shows the generated methods, renames are easier to track, and incorrect types are detected at compile time.
Master data is rarely maintained by only its original author. Another programmer may inherit it years later. Large balance changes may arrive near the end of production. In a live-service game, new tables and rows continue to accumulate.
If access logic is scattered across string keys and custom Dictionaries, the system becomes difficult to understand. A generated typed API preserves the intent of each lookup in code.
Composite Keys, Range Queries, and Closest-Value Queries Fit Game Data
Game master data often needs more than a simple ID lookup.
ID queries are still the foundation:
var item = db.ItemMasterTable.FindByItemId(itemId);
But production games regularly need queries such as:
- Retrieve a reward by stage ID and difficulty.
- Retrieve progression values by character ID and level.
- Retrieve products by shop ID and category.
- Retrieve rewards for a rank.
- Find the rating definition closest to a score.
- Retrieve active rows for an event ID.
- Retrieve experience values within a level range.
A Dictionary-only implementation tends to accumulate supporting structures:
Dictionary<int, ItemMaster> itemById;
Dictionary<ItemType, List<ItemMaster>> itemsByType;
Dictionary<(int stageId, Difficulty difficulty), StageRewardMaster> rewardByStageAndDifficulty;
Dictionary<int, List<LevelMaster>> levelsByCharacterId;
There is nothing inherently wrong with that approach. For a small project, a few custom Dictionaries may be the clearest solution.
As the table count, lookup patterns, and team size grow, however, common problems appear:
- Nobody is sure which Dictionary is the canonical lookup path.
- Multiple Dictionaries are created for the same purpose.
- Initialization order becomes complicated.
- Memory usage becomes difficult to understand.
- Rebuild logic is missed when data changes.
- Composite-key conventions differ across the project.
MasterMemory can generate unique and non-unique lookups, composite-key queries, range queries, and closest-value queries from PrimaryKey and SecondaryKey definitions.
The official README includes examples such as multi-key queries, FindClosestBy***, and FindRangeBy***.
This moves the decision about how a table should be queried closer to the schema itself. Lookup structures are less likely to spread throughout consumer code.
In a multi-programmer project, consistency has value by itself. If the team knows that a particular piece of data always comes from a particular generated table method, reviews become easier and unnecessary caches are less likely to proliferate.
The Value of Reducing Hand-Written Dictionaries
Discussions about MasterMemory often turn into a simple question: “Is it faster than Dictionary?”
Performance matters, but production value also comes from leaving behind a workflow where every new query requires another custom Dictionary.
A data repository that starts small can gradually turn into something like this:
public sealed class MasterDataRepository
{
Dictionary<int, ItemMaster> itemById;
Dictionary<int, SkillMaster> skillById;
Dictionary<int, CharacterMaster> characterById;
Dictionary<int, List<SkillMaster>> skillsByCharacterId;
Dictionary<(int stageId, int difficulty), StageMaster> stageByKey;
Dictionary<int, List<ShopItemMaster>> shopItemsByShopId;
}
This repository may initially be convenient. Over time, it often develops familiar problems:
- Initialization grows longer.
- Dictionary construction code accumulates.
- It becomes unclear which caches are actually required.
- Unused caches remain in memory.
- Every new query pattern adds another structure.
- Tests become harder to write.
- Validation logic becomes mixed with cache construction.
At that point, the data repository itself has become technical debt.
MasterMemory moves many of these basic lookup structures into the schema and generated code.
Project-specific convenience methods and cross-table queries will still be necessary. But the team no longer has to reimplement ID lookup, composite-key lookup, range lookup, and non-unique grouping every time.
That improves not only performance characteristics, but also architectural clarity.
Dictionary is still the better choice in some cases. If there are only a few tables, every query is a simple ID lookup, and the data is temporary or frequently replaced during runtime, a straightforward Dictionary may be easier to manage. The same may be true for editor tooling or debugging data that developers want to inspect and modify directly.
The goal is not to reject Dictionary. MasterMemory becomes valuable when lookup patterns and cache-construction code begin to scale faster than the team can manage comfortably.
Binary Data Simplifies the Runtime Loading Path
Reading CSV or JSON directly at runtime is convenient early in development.
CSV is easy to export from Excel or Google Sheets. JSON represents structure clearly and is easy to inspect while debugging.
In a production runtime, however, parsing CSV or JSON may introduce additional work:
- Parsing is required.
- String processing increases.
- Values must be converted to their target types.
- Errors appear at runtime.
- GC allocations may increase.
- Loading may take longer.
- Platform-specific behavior may surface.
For a small dataset, this may not matter. Keeping a lightweight configuration file as JSON can be the simpler choice for debug builds, operations tools, A/B test settings, or other small and frequently inspected data.
For a mobile game or a long-running live-service project where the amount of static data continually grows, runtime text parsing is often something worth reducing.
MasterMemory builds the data as a MessagePack binary package ahead of time. At runtime, the client retrieves that binary and constructs a MemoryDatabase.
var binary = await LoadMasterBinaryAsync();
var db = new MemoryDatabase(binary);
Binary data does not make MemoryDatabase construction free. Large datasets should still be profiled on target hardware. If necessary, the constructor's maxDegreeOfParallelism option can also be evaluated.
The runtime path becomes focused on three responsibilities: retrieve the binary, build the database, and query it.
CSV column resolution, string-to-integer conversion, enum conversion, date parsing, null handling, and empty-string rules no longer need to run as part of every client startup path.
Unity still needs a project-specific strategy for retrieving the binary.
MasterMemory's responsibility begins once the binary has been obtained. The project must decide whether it lives in Resources, StreamingAssets, Addressables, an AssetBundle, or a remote content-delivery system.
The application must also prevent database access before asynchronous initialization completes. If the game supports hot updates or event-specific dataset swaps, the team must define when the active database changes and how long consumers of the previous snapshot may continue to exist.
Production code should also decide what happens when loading fails:
- Stop startup and show an error
- Retry
- Continue using the last known-good database
A single reference point such as MasterDataProvider can help ensure that every system observes the same database generation. When a binary is obtained as a TextAsset through Addressables or AssetBundle, the lifetime of the handle and the data required after database construction should also be verified.
It is safer to avoid caching row objects or RangeView<T> values from an old database for long periods after a swap.
A clean division is:
- Convert and validate data before the build, or in CI.
- Load only validated binary data at runtime.
That separation makes the runtime code more predictable.
Writing Validators in C# Is Practical
The most dangerous master-data errors are often not failures to parse. They are values that parse successfully but describe an invalid game state.
Examples include:
- A reward item ID that does not exist
- A skill referencing a missing effect ID
- A gap in a level progression table
- A missing localization key
- A start date later than an end date
- Probabilities that do not add up to 100 percent
- Duplicate values inside a group that requires uniqueness
Types alone cannot catch these problems.
An integer may be valid as an integer while still referencing a nonexistent record. A string may be valid as a string while still naming a missing localization entry.
MasterMemory includes Validator support.
The Cygames article about MasterMemory v2 explains that a schema can implement IValidatable<T>, allowing validation logic to stay close to the data definition. It can validate reference-like relationships, numeric ranges, and rules involving the entire dataset.
A conceptual example that verifies a reward item reference may look like this:
[MemoryTable("quest"), MessagePackObject(true)]
public sealed class QuestMaster : IValidatable<QuestMaster>
{
[PrimaryKey]
public int QuestId { get; init; }
public int RewardItemId { get; init; }
void IValidatable<QuestMaster>.Validate(IValidator<QuestMaster> validator)
{
if (RewardItemId > 0)
{
var items = validator.GetReferenceSet<ItemMaster>();
// Conceptual example: verify that the current QuestMaster.RewardItemId
// exists as an ItemMaster.ItemId.
items.Exists(quest => quest.RewardItemId, item => item.ItemId);
}
// Additional validation can still run when RewardItemId <= 0.
}
}
Validator is easiest to reason about when it runs after database generation, before a build, or as part of the CI conversion pipeline. Its role is to stop invalid data before the game loads it, rather than discovering the problem after the runtime has already started.
Writing the rules in C# is flexible.
The team can use IDE completion, refactoring tools, compile-time type checking, and its normal code-review process instead of introducing a separate DSL or rule-file format.
Game-data validation also tends to exceed simple foreign-key checks:
- Another column is required only when a flag is true.
- Allowed ranges differ by rarity.
- The referenced table depends on the event type.
- Certain ID ranges are reserved.
- A value is allowed in development but forbidden in production.
- Consistency must be checked across several tables.
Those rules are often easier to maintain as normal program logic.
Validator therefore makes MasterMemory more than a runtime lookup database. It also gives the data pipeline a place to enforce data quality.
Metadata Leaves Room for Surrounding Tools
The official README also describes Metadata APIs that can be used when building custom importers and exporters.
This is less visible than lookup performance, but it matters in production.
A complete game-data workflow usually needs more than the runtime database:
- A CSV conversion tool
- A Google Sheets importer
- A diff viewer
- HTML or Markdown validation reports
- Readable CI error output
- A debug exporter that converts data back to JSON
- Planner-facing tools that identify the exact source of an error
When building these tools, it helps to inspect table and column definitions programmatically.
Metadata makes it possible to build a custom pipeline around MasterMemory.
That does not mean MasterMemory automatically provides a finished operational platform. It means the library exposes enough structure for teams to build the workflow they need.
This extensibility becomes valuable when MasterMemory is treated as one component of a larger data pipeline rather than only as a runtime database.
How It Compares with ScriptableObject
ScriptableObject remains a powerful option for static data in Unity.
It has a different set of strengths:
- Data is visible in the Unity Editor.
- Designers can edit it through the Inspector.
- It can be referenced as an Asset.
- It integrates naturally with Prefabs and Addressables.
- It can be convenient for artists and level designers.
There is no need to reject ScriptableObject.
For many kinds of data, ScriptableObject is the better tool:
- A small number of configuration objects
- Data that benefits from references between ScriptableObjects
- Data that should be edited visually in the Unity Editor
- Data that directly references Unity Assets
- Presentation settings for individual levels
Managing a large tabular dataset entirely with ScriptableObject becomes more difficult:
- One Asset per record creates many files.
- The data is difficult to view as a complete table.
- Spreadsheet-style editing is awkward for planners.
- Diffs are harder to review.
- Bulk conversion and validation require additional tooling.
- ID and composite-key queries still need to be implemented.
MasterMemory is better suited to large tabular datasets that need fast, type-safe lookup.
For Asset references, a MasterMemory row can store an Addressables key, GUID, Resources path, or project-specific Asset ID, while another layer resolves that identifier to the actual Unity Asset. Separating tabular game data from Asset resolution often produces a cleaner workflow than forcing both responsibilities into the same system.
ScriptableObject and MasterMemory are therefore less direct competitors than they first appear. Their strengths differ:
- Use ScriptableObject for a small number of editor-friendly configuration Assets.
- Use MasterMemory for large tabular datasets that require structured, type-safe lookup.
How It Compares with SQLite
SQLite is another established option for a local database.
It has a long track record, supports flexible SQL queries, and benefits from a mature ecosystem of tools.
For client-side static game data, however, that flexibility may be more than the project needs.
Most games do not construct arbitrary SQL queries during gameplay. They usually perform a known set of lookups:
- Find by ID
- Find by composite key
- Retrieve all rows for a group ID
- Retrieve a range
- Retrieve the closest value
For those patterns, type-safe generated APIs, low allocation, fast loading, and compact memory usage may be more useful than unrestricted SQL.
String-based SQL also means that some query errors remain invisible until runtime.
MasterMemory defines the lookup patterns ahead of time and exposes generated methods.
It is less flexible than SQLite, but that trade-off can make it easier to use for read-only game data.
The point is not that one tool is universally better.
SQLite may be the right choice when runtime updates or flexible ad hoc queries are required. MasterMemory is a better fit when the application reads a large, predefined dataset through known query paths.
When MasterMemory Is—and Is Not—a Good Fit
The following table summarizes the decision:
| Consideration | MasterMemory is a good fit | Another approach may be simpler |
|---|---|---|
| Data size | Many tables or records | Only a small amount of data |
| Update frequency | Mostly immutable during a session | Mutated frequently at runtime |
| Queries | Many ID, composite-key, or range queries | Only a few simple lookups |
| Performance | Startup time, GC, or memory usage matters | Performance is not currently a concern |
| Type safety | Query mistakes should be caught at compile time | Minimal setup is more important |
| Validation | Cross-table references and ranges need C# validation | Manual inspection is sufficient |
| Pipeline | Conversion and validation should run in CI | Local manual work is sufficient |
| Existing architecture | Custom Dictionaries have grown complex | ScriptableObject already works well |
The most important considerations are usually data size, lookup patterns, and update frequency.
If the dataset is large, the game uses more than simple ID lookups, and the active data is effectively immutable during a session, MasterMemory is a natural candidate.
If ScriptableObject already handles a small dataset comfortably, if the data is temporary and frequently mutated, or if the project requires arbitrary SQL queries, another approach may remain simpler.
Adoption also has a cost.
A MasterMemory project needs schema management based on Source Generators and MessagePack attributes. Migrating from ScriptableObject, CSV, or JSON may require conversion tools and a validation pipeline.
Schema changes also require the team to consider binary regeneration, compatibility, and consistency with already distributed data. In Unity, package updates, generated code, IL2CPP/AOT behavior, and CI generation steps become part of the operational process.
The Unity version must support the Source Generator workflow, and package installation generally involves NuGetForUnity or an equivalent setup. At the time of writing, the v3.0.4 README lists Unity 2022.3.12f1 as the minimum supported version.
If examples use C# 9 init accessors, some Unity configurations may require an IsExternalInit definition so the compiler can resolve that feature. In IL2CPP builds, the generated MasterMemoryResolver must also be registered with MessagePack's Resolver configuration.
This is not an installation tutorial, so the exact setup is left to the official README and version-specific guides. The important point is that a project can appear to work in the Editor and still fail later in an IL2CPP device build if these requirements are ignored.
MasterMemory is not a magic box that instantly solves every master-data problem.
What it does provide is a clear owner for the runtime read-only database layer, which makes the rest of the runtime architecture easier to organize.
What MasterMemory Does Not Solve
So far, this article has focused primarily on MasterMemory's strengths.
The remaining areas are not necessarily weaknesses. They are responsibilities outside the scope of the library.
MasterMemory is effective at providing fast, type-safe runtime access to static data. A production game-data workflow also has stages before and after that runtime database.
| Area | Decisions MasterMemory does not make for you |
|---|---|
| Source of truth | Whether Excel, Google Sheets, or CSV is authoritative |
| Editing workflow | How planners edit and review changes |
| Conversion | How source rows become C# objects and then a database binary |
| Localization | How keys, missing translations, per-language CSV files, and translation returns are handled |
| Diff review | How humans compare revisions |
| Debugging | How a binary record maps back to its source row |
| CI/CD | How conversion, validation, and failure reporting are automated |
| Environments | How development, QA, production, and pre-release event data are separated |
If Google Sheets is the source of truth, the project still needs code that retrieves the sheets through an API, converts rows to typed objects, and passes them to MasterMemory's DatabaseBuilder.
If Excel is the source of truth, the team still needs rules for reading files, interpreting sheet and column names, handling blank rows and comments, and converting values to their target types.
Localization is another separate workflow.
Text keys can be stored in MasterMemory, but the library does not decide how to export per-language CSV files, detect missing translations, remove obsolete keys, exchange files with a translation vendor, or review changes when translations return.
Binary data is efficient for the runtime but inconvenient for humans to inspect directly.
A production workflow often needs tools that answer questions such as:
- Did the number of records remain consistent after conversion?
- How was a specific ID transformed?
- What changed since the previous build?
- Did development-only data leak into the production package?
- Is the device actually using the latest dataset?
- Which row in the original spreadsheet caused the validation error?
Validator is useful for data correctness, but planner and QA workflows such as reviewing diffs, inspecting conversion results, and tracing an error back to a source row usually require additional tools.
CI/CD and environment-specific data require the same kind of explicit design.
MasterMemory can be the final database that the client reads, but the project still decides which source generates which binary, under what conditions, and where that binary is deployed.
Leaving those decisions vague can lead to serious mistakes: development data being shipped to production, builds using stale binaries, or unvalidated data entering a release.
The distinction matters.
Adopting MasterMemory does not automate the entire lifecycle of game data. It does, however, give the runtime lookup layer a clear and reliable shape.
Once that layer is stable, the surrounding import, validation, conversion, deployment, diff, and debugging pipeline becomes easier to design deliberately.
Conclusion
MasterMemory is a strong option for Unity and .NET projects that need to handle a large amount of read-only static game data.
Its Source Generator creates type-safe query APIs, while composite-key, range, and closest-value queries, MessagePack binary loading, Validator, Metadata, and string interning help organize runtime data access. Read-only behavior is also a good fit for game data because the active database can be treated as an immutable snapshot.
The source of truth in Excel or Google Sheets, planner-facing editing, localization, diff review, CI conversion, environment-specific deployment, and debugging remain responsibilities of the surrounding pipeline.
That is not a defect so much as a boundary of responsibility.
Evaluating MasterMemory as a fast, type-safe, read-only database, rather than as a complete master-data management platform, makes adoption decisions much clearer. Its runtime strengths become most valuable when they are combined with a deliberate pipeline for importing, converting, validating, reviewing, and distributing the data around it.
Top comments (0)