Skip to content

Reduced entity kill data by skipping default attributes - #1028

Merged
Intelli merged 2 commits into
PlayPro:masterfrom
tonyjamesstark:skip-default-attributes
Oct 5, 2026
Merged

Intelli merged 2 commits into
PlayPro:masterfrom
tonyjamesstark:skip-default-attributes

Conversation

@tonyjamesstark

Copy link
Copy Markdown
Contributor

Each mob kill stores every registered attribute: about 23 entries on 1.21+, almost all at their default value. They make up about 88% of each blob. This PR stores an attribute only when its base value differs from the default, or when it has modifiers.

How much it saves on SQLite: a table leaf page is 4096 bytes and holds at most 4061 bytes of row data. A zombie kill blob is 2036 bytes today, so two rows do not fit and every kill uses a whole page. At 1027 bytes, three rows fit on a page. Measured with dbstat over 20,000 rows:

Zombie kill Blob Rows per page Disk per kill
Current 2036 B 1 4096 B
This PR 1027 B 3 1365 B
This PR after the v26 migration to EntityDataCodec 141 B 26 158 B

This PR is proposed before v26 because it is the only size relief for SQLite and MySQL until then. After v26, it still makes each blob about 1.8 times smaller (250 to 141 bytes). If you prefer to skip default attributes as part of the v26 migration, I can adjust or withdraw it.

Rollback stays exact. New rows carry a marker at kill data index 9. When restore sees the marker, it first resets every attribute of the respawned mob to its default and removes spawn-time modifiers, then applies the stored ones. Index 7 (UUID or null) and index 8 (the kill-location slot for placed entities) keep their meaning. Rows without the marker restore as before. Capture and restore both use AttributeInstance#getDefaultValue(), which exists on every supported version (1.16.5+). It is deprecated since 26.1 but is not scheduled for removal.
VinVQJq4te1f

authored and tested with claude

EntityDeathListener stored every registered attribute of a killed mob,
about 23 entries on 1.21+, although almost all hold default values. The
attribute list made up about 88% of each kill blob.

Store an attribute only when its base value differs from the default or
it has modifiers. Kill data now always carries index 7 (UUID or null),
index 8 (kill location slot, null for mobs) and a format marker at index
9. When restore sees the marker, it first resets every attribute of the
respawned entity to its default base value and removes spawn-time
modifiers, then applies the stored attributes, so rollback reproduces
the logged entity exactly. Rows without the marker restore as before.

Capture and restore both use AttributeInstance#getDefaultValue. It is
deprecated since 26.1 for returning the server default rather than the
entity type default, but it exists on every supported version (1.16.5+)
and the same value is used on both sides.

Zombie kill blob: 2036 to 1027 bytes in the legacy format, and 250 to
141 bytes with EntityDataCodec (DuckDB and ClickHouse today, SQLite and
MySQL after the v26 migration). On SQLite that is 4096 to 1365 bytes per
kill on disk now, or 158 bytes after v26.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PdEuKbjcVoVinVQJq4te1f
@Intelli

Intelli commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Thanks! The storage reduction looks worthwhile, but the sparse format needs to preserve correctness across server upgrades before this can merge.

  • resetAttributes() currently resets every attribute available on the restore server. For example, a horse logged before 1.20.5 and restored on 26.1 would have its step height, safe fall distance, and fall damage multiplier reset to global defaults of 0.6/3/1 instead of the horse defaults of 1/6/0.5. Those attributes did not exist in the original record, so they should retain their normal spawn values.
  • Please make the sparse format identify which attributes were omitted and reconstruct their original baseline values independently of the current server’s defaults. Attributes absent from the original snapshot should remain untouched. An immutable, versioned baseline with explicit storage for attributes outside that baseline could work. Please retain the existing behavior for unmarked records.

Restoring sparse kill data reset every attribute of the respawned entity
to the restoring server's defaults. An attribute the logging server did
not have yet was reset as well: a horse logged before 1.20.5 and
restored on 26.1 got the global step height, safe fall distance and fall
damage multiplier instead of the horse values.

AttributeUtils now holds a frozen baseline of 40 attribute values, taken
from the 26.2 registry defaults. Kill data leaves out an attribute only
when it is in the baseline, has the baseline value and has no modifiers,
and sets that attribute's bit in a long stored after the format marker
at index 10. Restore sets exactly the attributes with a bit to their
baseline values and leaves every other attribute as spawned. Entries
are only ever appended, so stored bits keep their meaning, and the
restoring server's defaults are never read. Rows without the marker
restore as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Intelli
Intelli merged commit 4b1e9ab into PlayPro:master Oct 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants