RLE Encoding Code Cleanup [PTO]

* For the doodad tool: skip the assets embed folder, the doodad binary doesn't
  need to include all the game's doodads/levelpacks/etc. and can save on file
  size.
* In `doodad resave`, .doodad files with Vacuum() and upgrade their chunker from
  the MapAccessor to the RLEAccessor.
* Fix a rare concurrent map read/write error in OptimizeChunkerAccessors.
This commit is contained in:
Noah 2024-05-24 15:03:32 -07:00
parent 4851730ccf
commit 6be2f86b58
9 changed files with 77 additions and 6 deletions

View File

@ -15,6 +15,7 @@
- [Build on macOS from scratch](#build-on-macos-from-scratch) - [Build on macOS from scratch](#build-on-macos-from-scratch)
- [WebAssembly](#webassembly) - [WebAssembly](#webassembly)
- [Build Tags](#build-tags) - [Build Tags](#build-tags)
- [doodad](#doodad)
- [dpp](#dpp) - [dpp](#dpp)
# Dockerfile # Dockerfile
@ -373,6 +374,13 @@ Some tips to get a WASM build to work:
Go build tags used by this game: Go build tags used by this game:
## doodad
This tag is used when building the `doodad` command-line tool.
It ensures that the embedded bindata assets (built-in doodads, etc.) do not
need to be bundled into the doodad binary, but only the main game binary.
## dpp ## dpp
The dpp tag stands for Doodle++ and is used for official commercial builds of The dpp tag stands for Doodle++ and is used for official commercial builds of

View File

@ -23,7 +23,7 @@ setup: clean
.PHONY: build .PHONY: build
build: build:
go build $(LDFLAGS) $(BUILD_TAGS) -o bin/sketchymaze cmd/doodle/main.go go build $(LDFLAGS) $(BUILD_TAGS) -o bin/sketchymaze cmd/doodle/main.go
go build $(LDFLAGS) $(BUILD_TAGS) -o bin/doodad cmd/doodad/main.go go build $(LDFLAGS) -tags=doodad -o bin/doodad cmd/doodad/main.go
# `make buildall` to run all build steps including doodads. # `make buildall` to run all build steps including doodads.
.PHONY: buildall .PHONY: buildall
@ -34,7 +34,7 @@ buildall: doodads build
build-free: build-free:
gofmt -w . gofmt -w .
go build $(LDFLAGS) -o bin/sketchymaze cmd/doodle/main.go go build $(LDFLAGS) -o bin/sketchymaze cmd/doodle/main.go
go build $(LDFLAGS) -o bin/doodad cmd/doodad/main.go go build $(LDFLAGS) -tags=doodad -o bin/doodad cmd/doodad/main.go
# `make bindata` generates the embedded binary assets package. # `make bindata` generates the embedded binary assets package.
.PHONY: bindata .PHONY: bindata
@ -75,7 +75,7 @@ mingw:
go build $(LDFLAGS_W) $(BUILD_TAGS) -o bin/sketchymaze.exe cmd/doodle/main.go go build $(LDFLAGS_W) $(BUILD_TAGS) -o bin/sketchymaze.exe cmd/doodle/main.go
env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \ env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \ GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
go build $(LDFLAGS) $(BUILD_TAGS) -o bin/doodad.exe cmd/doodad/main.go go build $(LDFLAGS) -tags=doodad -o bin/doodad.exe cmd/doodad/main.go
# `make mingw32` to cross-compile a Windows binary with mingw (32-bit). # `make mingw32` to cross-compile a Windows binary with mingw (32-bit).
.PHONY: mingw32 .PHONY: mingw32
@ -85,7 +85,7 @@ mingw32:
go build $(LDFLAGS_W) $(BUILD_TAGS) -o bin/sketchymaze.exe cmd/doodle/main.go go build $(LDFLAGS_W) $(BUILD_TAGS) -o bin/sketchymaze.exe cmd/doodle/main.go
env CGO_ENABLED="1" CC="/usr/bin/i686-w64-mingw32-gcc" \ env CGO_ENABLED="1" CC="/usr/bin/i686-w64-mingw32-gcc" \
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \ GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
go build $(LDFLAGS) $(BUILD_TAGS) -o bin/doodad.exe cmd/doodad/main.go go build $(LDFLAGS) -tags=doodad -o bin/doodad.exe cmd/doodad/main.go
# `make mingw-free` for Windows binary in free mode. # `make mingw-free` for Windows binary in free mode.
.PHONY: mingw-free .PHONY: mingw-free
@ -95,7 +95,7 @@ mingw-free:
go build $(LDFLAGS_W) -o bin/sketchymaze.exe cmd/doodle/main.go go build $(LDFLAGS_W) -o bin/sketchymaze.exe cmd/doodle/main.go
env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \ env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \ GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
go build $(LDFLAGS) -o bin/doodad.exe cmd/doodad/main.go go build $(LDFLAGS) -tags=doodad -o bin/doodad.exe cmd/doodad/main.go
# `make release` runs the release.sh script, must be run # `make release` runs the release.sh script, must be run
# after `make dist` # after `make dist`

View File

@ -1,3 +1,6 @@
//go:build !doodad
// +build !doodad
// Package assets gets us off go-bindata by using Go 1.16 embed support. // Package assets gets us off go-bindata by using Go 1.16 embed support.
// //
// For Go 1.16 embed, this source file had to live inside the assets/ folder // For Go 1.16 embed, this source file had to live inside the assets/ folder

32
assets/assets_omitted.go Normal file
View File

@ -0,0 +1,32 @@
//go:build doodad
// +build doodad
// Dummy version of assets_embed.go that doesn't embed any files.
// For the `doodad` tool.
package assets
import (
"embed"
"errors"
)
var Embedded embed.FS
var errNotEmbedded = errors.New("assets not embedded")
// AssetDir returns the list of embedded files at the directory name.
func AssetDir(name string) ([]string, error) {
return nil, errNotEmbedded
}
// Asset returns the byte data of an embedded asset.
func Asset(name string) ([]byte, error) {
return nil, errNotEmbedded
}
// AssetNames dumps the names of all embedded assets,
// with their legacy "assets/" prefix from go-bindata.
func AssetNames() []string {
return nil
}

View File

@ -101,6 +101,12 @@ func resaveDoodad(c *cli.Context, filename string) error {
filename = output filename = output
} }
if err := dd.Vacuum(); err != nil {
log.Error("Vacuum error: %s", err)
} else {
log.Info("Run vacuum on doodad file.")
}
log.Info("Saving back to disk") log.Info("Saving back to disk")
if err := dd.WriteJSON(filename); err != nil { if err := dd.WriteJSON(filename); err != nil {
return fmt.Errorf("couldn't write %s: %s", filename, err) return fmt.Errorf("couldn't write %s: %s", filename, err)

View File

@ -9,7 +9,7 @@ import (
const ( const (
AppName = "Sketchy Maze" AppName = "Sketchy Maze"
Summary = "A drawing-based maze game" Summary = "A drawing-based maze game"
Version = "0.14.0" Version = "0.14.1"
Website = "https://www.sketchymaze.com" Website = "https://www.sketchymaze.com"
Copyright = "2023 Noah Petherbridge" Copyright = "2023 Noah Petherbridge"
Byline = "a game by Noah Petherbridge." Byline = "a game by Noah Petherbridge."

View File

@ -0,0 +1,14 @@
package doodads
// 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 *Doodad) Vacuum() error {
// Let the Chunker optimize accessor types.
for _, layer := range m.Layers {
layer.Chunker.OptimizeChunkerAccessors()
}
return nil
}

View File

@ -164,6 +164,11 @@ func (d *Doodad) WriteFile(filename string) error {
d.Version = 1 d.Version = 1
d.GameVersion = branding.Version d.GameVersion = branding.Version
// Maintenance functions, clean up cruft before save.
if err := d.Vacuum(); err != nil {
log.Error("Vacuum level %s: %s", filename, err)
}
bin, err := d.ToJSON() bin, err := d.ToJSON()
if err != nil { if err != nil {
return err return err

View File

@ -36,8 +36,11 @@ func (c *Chunker) OptimizeChunkerAccessors() {
ma, _ := chunk.Accessor.(*MapAccessor) ma, _ := chunk.Accessor.(*MapAccessor)
rle := NewRLEAccessor(chunk).FromMapAccessor(ma) rle := NewRLEAccessor(chunk).FromMapAccessor(ma)
// Lock the chunker for updating.
c.chunkMu.Lock()
c.Chunks[point].Type = RLEType c.Chunks[point].Type = RLEType
c.Chunks[point].Accessor = rle c.Chunks[point].Accessor = rle
c.chunkMu.Unlock()
} }
} }
} }