Noah Petherbridge
5654145fd8
Finally add a second option for Chunk MapAccessor implementation besides the MapAccessor. The RLEAccessor is basically a MapAccessor that will compress your drawing with Run Length Encoding (RLE) in the on-disk format in the ZIP file. This slashes the file sizes of most levels: * Shapeshifter: 21.8 MB -> 8.1 MB * Jungle: 10.4 MB -> 4.1 MB * Zoo: 2.8 MB -> 1.3 MB Implementation details: * The RLE binary format for Chunks is a stream of Uvarint pairs storing the palette index number and the number of pixels to repeat it (along the Y,X axis of the chunk). * Null colors are represented by a Uvarint that decodes to 0xFFFF or 65535 in decimal. * Gameplay logic currently limits maps to 256 colors. * The default for newly created chunks in-game will be RLE by default. * Its in-memory representation is still a MapAccessor (a map of absolute world coordinates to palette index). * The game can still open and play legacy MapAccessor maps. * On save in the editor, the game will upgrade/convert MapAccessor chunks over to RLEAccessors, improving on your level's file size with a simple re-save. Current Bugs * On every re-save to RLE, one pixel is lost in the bottom-right corner of each chunk. Each subsequent re-save loses one more pixel to the left, so what starts as a single pixel per chunk slowly evolves into a horizontal line. * Some pixels smear vertically as well. * Off-by-negative-one errors when some chunks Iter() their pixels but compute a relative coordinate of (-1,0)! Some mismatch between the stored world coords of a pixel inside the chunk vs. the chunk's assigned coordinate by the Chunker: certain combinations of chunk coord/abs coord. To Do * The `doodad touch` command should re-save existing levels to upgrade them.
51 lines
1.3 KiB
Go
51 lines
1.3 KiB
Go
package level
|
|
|
|
import "git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
|
|
|
// Maintenance functions for the file format on disk.
|
|
|
|
// Vacuum runs any maintenance or migration tasks for the level at time of save.
|
|
//
|
|
// It will prune broken links between actors, or migrate internal data structures
|
|
// to optimize storage on disk of its binary data.
|
|
func (m *Level) Vacuum() error {
|
|
if links := m.PruneLinks(); links > 0 {
|
|
log.Debug("Vacuum: removed %d broken links between actors in this level.")
|
|
}
|
|
|
|
// Let the Chunker optimize accessor types.
|
|
m.Chunker.OptimizeChunkerAccessors()
|
|
|
|
return nil
|
|
}
|
|
|
|
// PruneLinks cleans up any Actor Links that can not be resolved in the
|
|
// level data. For example, if actors were linked in Edit Mode and one
|
|
// actor is deleted leaving a broken link.
|
|
//
|
|
// Returns the number of broken links pruned.
|
|
//
|
|
// This is called automatically in WriteFile.
|
|
func (m *Level) PruneLinks() int {
|
|
var count int
|
|
for id, actor := range m.Actors {
|
|
var newLinks []string
|
|
|
|
for _, linkID := range actor.Links {
|
|
if _, ok := m.Actors[linkID]; !ok {
|
|
log.Warn("Level.PruneLinks: actor %s (%s) was linked to unresolved actor %s",
|
|
id,
|
|
actor.Filename,
|
|
linkID,
|
|
)
|
|
count++
|
|
continue
|
|
}
|
|
newLinks = append(newLinks, linkID)
|
|
}
|
|
|
|
actor.Links = newLinks
|
|
}
|
|
return count
|
|
}
|