WIP Doodle++ #93
123
Building.md
123
Building.md
|
@ -1,12 +1,21 @@
|
|||
# Building Doodle
|
||||
|
||||
* [Automated Release Scripts](#automated-release-scripts)
|
||||
* [Quickstart with bootstrap.py](#quickstart-with-bootstrap-py)
|
||||
* [Detailed Instructions](#detailed-instructions)
|
||||
* [Linux](#linux)
|
||||
* [Flatpak for Linux](#flatpak-for-linux)
|
||||
* [Windows Cross-Compile from Linux](#windows-cross-compile-from-linux)
|
||||
* [Old Docs](#old-docs)
|
||||
- [Building Doodle](#building-doodle)
|
||||
- [Dockerfile](#dockerfile)
|
||||
- [Automated Release Scripts](#automated-release-scripts)
|
||||
- [Go Environment](#go-environment)
|
||||
- [Quickstart with bootstrap.py](#quickstart-with-bootstrappy)
|
||||
- [Detailed Instructions](#detailed-instructions)
|
||||
- [Fonts](#fonts)
|
||||
- [Makefile](#makefile)
|
||||
- [Dependencies](#dependencies)
|
||||
- [Flatpak for Linux](#flatpak-for-linux)
|
||||
- [Windows Cross-Compile from Linux](#windows-cross-compile-from-linux)
|
||||
- [Windows DLLs](#windows-dlls)
|
||||
- [Build on macOS from scratch](#build-on-macos-from-scratch)
|
||||
- [WebAssembly](#webassembly)
|
||||
- [Build Tags](#build-tags)
|
||||
- [dpp](#dpp)
|
||||
|
||||
# Dockerfile
|
||||
|
||||
|
@ -43,6 +52,21 @@ Other Dockerfiles and scripts used to release the game:
|
|||
The Docker container depends on all the git servers being up, but if you have
|
||||
the uber blob source code you can read the Dockerfile to see what it does.
|
||||
|
||||
# Go Environment
|
||||
|
||||
Part of the build scripts involve building and running the `doodad` command
|
||||
from this repo in order to generate the game's built-in doodads. For this to
|
||||
work smoothly from your Linux or macOS build environment, you may need to
|
||||
ensure that your `${GOPATH}/bin` directory is on your `$PATH` by, for example,
|
||||
configuring this in your bash/zsh profile:
|
||||
|
||||
```bash
|
||||
export GOPATH="${HOME}/go"
|
||||
export PATH="${PATH}:${GOPATH}/bin"
|
||||
```
|
||||
|
||||
For a complete example, see the "Build on macOS from scratch" section below.
|
||||
|
||||
# Quickstart with bootstrap.py
|
||||
|
||||
From any Unix-like system (Fedora, Ubuntu, macOS) the bootstrap.py script
|
||||
|
@ -278,6 +302,47 @@ cp /usr/x86_64-w64-mingw32/bin/SDL*.dll bin/
|
|||
SDL2_ttf requires libfreetype, you can get its DLL here:
|
||||
https://github.com/ubawurinna/freetype-windows-binaries
|
||||
|
||||
## Build on macOS from scratch
|
||||
|
||||
Here are some detailed instructions how to build Sketchy Maze from a fresh
|
||||
install of macOS Ventura that assumes no previous software or configuration
|
||||
was applied to the system yet.
|
||||
|
||||
Install homebrew: https://brew.sh pay attention to the instructions at the end
|
||||
of the install to set up your zsh profile for homebrew to work correctly.
|
||||
|
||||
Clone the doodle repository:
|
||||
|
||||
```bash
|
||||
git clone https://git.kirsle.net/SketchyMaze/doodle
|
||||
cd doodle
|
||||
```
|
||||
|
||||
Note: on a fresh install, invoking the `git` command may cause macOS to install
|
||||
developer tools and Xcode. After installed, run the git clone again to finish
|
||||
cloning the repository.
|
||||
|
||||
Set your Go environment variables: edit your ~/.zprofile and ensure that $GOPATH
|
||||
is configured and that your $PATH includes $GOPATH/bin. **Note:** restart your
|
||||
terminal session or reload the config file (e.g. `. ~/.zprofile`) after making
|
||||
this change.
|
||||
|
||||
```bash
|
||||
# in your .zprofile, .bash_profile, .zshrc or similar shell config
|
||||
export GOPATH="${HOME}/go"
|
||||
export PATH="${PATH}:${GOPATH}/bin"
|
||||
```
|
||||
|
||||
Run the bootstrap script:
|
||||
|
||||
```bash
|
||||
python3 bootstrap.py
|
||||
```
|
||||
|
||||
Answer N (default) when asked to clone dependency repos over ssh. The bootstrap
|
||||
script will `brew install` any necessary dependencies (Go, SDL2, etc.) and clone
|
||||
support repos for the game (doodads, levelpacks, assets).
|
||||
|
||||
# WebAssembly
|
||||
|
||||
There is some **experimental** support for a WebAssembly build of Sketchy Maze
|
||||
|
@ -304,33 +369,33 @@ Some tips to get a WASM build to work:
|
|||
* Run `make wasm` to build the WASM binary and `make wasm-serve` to run a simple
|
||||
Go web server to serve it from.
|
||||
|
||||
# Old Docs
|
||||
# Build Tags
|
||||
|
||||
## Build Tags
|
||||
Go build tags used by this game:
|
||||
|
||||
These aren't really used much anymore but documented here:
|
||||
## dpp
|
||||
|
||||
### shareware
|
||||
The dpp tag stands for Doodle++ and is used for official commercial builds of
|
||||
the game. Doodle++ builds include additional code not found in the free & open
|
||||
source release of the game engine.
|
||||
|
||||
> Files ending with `_free.go` are for the shareware release as opposed to
|
||||
> `_paid.go` for the full version.
|
||||
This build tag should be set automatically by the Makefile **if** the deps/
|
||||
folder has a git clone of the dpp project. The bootstrap.py script will clone
|
||||
the dpp repo **if** you use SSH to clone dependencies: so you will need SSH
|
||||
credentials to the upstream git server. It basically means that third-party
|
||||
users who download the open source release will not have the dpp dependency,
|
||||
and will not build dpp copies of the game.
|
||||
|
||||
Builds the game in the free shareware release mode.
|
||||
If you _do_ have the dpp dependency, you can force build (and run) FOSS
|
||||
versions of the game via the Makefile commands `make build-free`,
|
||||
`make run-free` or `make dist-free` which are counterparts to the main make
|
||||
commands but which deliberately do not set the dpp build tag.
|
||||
|
||||
Run `make build-free` to build the shareware binary.
|
||||
In source code, files ending with `_dpp.go` and `_foss.go` are conditionally
|
||||
compiled depending on this build tag.
|
||||
|
||||
Shareware releases of the game have the following changes compared to the default
|
||||
(release) mode:
|
||||
How to tell whether your build of Sketchy Maze is Doodle++ include:
|
||||
|
||||
* No access to the Doodad Editor scene in-game (soft toggle)
|
||||
|
||||
### developer
|
||||
|
||||
> Files ending with `_developer.go` are for the developer build as opposed to
|
||||
> `_release.go` for the public version.
|
||||
|
||||
Developer builds support extra features over the standard release version:
|
||||
|
||||
* Ability to write the JSON file format for Levels and Doodads.
|
||||
|
||||
Run `make build-debug` to build a developer version of the program.
|
||||
* The version string on the title screen.
|
||||
* FOSS builds (not dpp) will say "open source" in the version.
|
||||
* DPP builds may say "shareware" if unregistered or just the version.
|
||||
|
|
40
Makefile
40
Makefile
|
@ -9,6 +9,12 @@ CURDIR=$(shell curdir)
|
|||
LDFLAGS := -ldflags "-X main.Build=$(BUILD) -X main.BuildDate=$(BUILD_DATE)"
|
||||
LDFLAGS_W := -ldflags "-X main.Build=$(BUILD) -X main.BuildDate=$(BUILD_DATE) -H windowsgui"
|
||||
|
||||
# Doodle++ build tag for official builds of the game.
|
||||
BUILD_TAGS := -tags=""
|
||||
ifneq ("$(wildcard ./deps/dpp)", "")
|
||||
BUILD_TAGS = -tags="dpp"
|
||||
endif
|
||||
|
||||
# `make setup` to set up a new environment, pull dependencies, etc.
|
||||
.PHONY: setup
|
||||
setup: clean
|
||||
|
@ -17,8 +23,8 @@ setup: clean
|
|||
# `make build` to build the binary.
|
||||
.PHONY: build
|
||||
build:
|
||||
go build $(LDFLAGS) -o bin/sketchymaze cmd/doodle/main.go
|
||||
go build $(LDFLAGS) -o bin/doodad cmd/doodad/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
|
||||
|
||||
# `make buildall` to run all build steps including doodads.
|
||||
.PHONY: buildall
|
||||
|
@ -28,15 +34,8 @@ buildall: doodads build
|
|||
.PHONY: build-free
|
||||
build-free:
|
||||
gofmt -w .
|
||||
go build $(LDFLAGS) -tags="shareware" -o bin/sketchymaze cmd/doodle/main.go
|
||||
go build $(LDFLAGS) -tags="shareware" -o bin/doodad cmd/doodad/main.go
|
||||
|
||||
# `make build-debug` to build the binary in developer mode.
|
||||
.PHONY: build-debug
|
||||
build-debug:
|
||||
gofmt -w .
|
||||
go build $(LDFLAGS) -tags="developer" -o bin/sketchymaze cmd/doodle/main.go
|
||||
go build $(LDFLAGS) -tags="developer" -o bin/doodad cmd/doodad/main.go
|
||||
go build $(LDFLAGS) -o bin/sketchymaze cmd/doodle/main.go
|
||||
go build $(LDFLAGS) -o bin/doodad cmd/doodad/main.go
|
||||
|
||||
# `make bindata` generates the embedded binary assets package.
|
||||
.PHONY: bindata
|
||||
|
@ -74,30 +73,30 @@ doodads:
|
|||
mingw:
|
||||
env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \
|
||||
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
|
||||
go build $(LDFLAGS_W) -i -o bin/sketchymaze.exe cmd/doodle/main.go
|
||||
go build $(LDFLAGS_W) $(BUILD_TAGS) -i -o bin/sketchymaze.exe cmd/doodle/main.go
|
||||
env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \
|
||||
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
|
||||
go build $(LDFLAGS) -i -o bin/doodad.exe cmd/doodad/main.go
|
||||
go build $(LDFLAGS) $(BUILD_TAGS) -i -o bin/doodad.exe cmd/doodad/main.go
|
||||
|
||||
# `make mingw32` to cross-compile a Windows binary with mingw (32-bit).
|
||||
.PHONY: mingw32
|
||||
mingw32:
|
||||
env CGO_ENABLED="1" CC="/usr/bin/i686-w64-mingw32-gcc" \
|
||||
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
|
||||
go build $(LDFLAGS_W) -i -o bin/sketchymaze.exe cmd/doodle/main.go
|
||||
go build $(LDFLAGS_W) $(BUILD_TAGS) -i -o bin/sketchymaze.exe cmd/doodle/main.go
|
||||
env CGO_ENABLED="1" CC="/usr/bin/i686-w64-mingw32-gcc" \
|
||||
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
|
||||
go build $(LDFLAGS) -i -o bin/doodad.exe cmd/doodad/main.go
|
||||
go build $(LDFLAGS) $(BUILD_TAGS) -i -o bin/doodad.exe cmd/doodad/main.go
|
||||
|
||||
# `make mingw-free` for Windows binary in free mode.
|
||||
.PHONY: mingw-free
|
||||
mingw-free:
|
||||
env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \
|
||||
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
|
||||
go build $(LDFLAGS_W) -tags="shareware" -i -o bin/sketchymaze.exe cmd/doodle/main.go
|
||||
go build $(LDFLAGS_W) -i -o bin/sketchymaze.exe cmd/doodle/main.go
|
||||
env CGO_ENABLED="1" CC="/usr/bin/x86_64-w64-mingw32-gcc" \
|
||||
GOOS="windows" CGO_LDFLAGS="-lmingw32 -lSDL2" CGO_CFLAGS="-D_REENTRANT" \
|
||||
go build $(LDFLAGS) -tags="shareware" -i -o bin/doodad.exe cmd/doodad/main.go
|
||||
go build $(LDFLAGS) -i -o bin/doodad.exe cmd/doodad/main.go
|
||||
|
||||
# `make release` runs the release.sh script, must be run
|
||||
# after `make dist`
|
||||
|
@ -145,12 +144,17 @@ from-docker32: build mingw32 __dist-common
|
|||
# `make run` to run it from source.
|
||||
.PHONY: run
|
||||
run:
|
||||
go run ${BUILD_TAGS} cmd/doodle/main.go
|
||||
|
||||
# `make run-free` to run it from source with no build tags (foss version).
|
||||
.PHONY: run-free
|
||||
run-free:
|
||||
go run cmd/doodle/main.go
|
||||
|
||||
# `make debug` to run it in -debug mode.
|
||||
.PHONY: debug
|
||||
debug:
|
||||
go run cmd/doodle/main.go -debug
|
||||
go run $(BUILD_TAGS) cmd/doodle/main.go -debug
|
||||
|
||||
# `make guitest` to run it in guitest mode.
|
||||
.PHONY: guitest
|
||||
|
|
|
@ -37,6 +37,10 @@ repos_github = {
|
|||
"git@github.com:kirsle/audio": "audio",
|
||||
# TODO: the rest
|
||||
}
|
||||
repos_ssh = {
|
||||
# SSH-only (private) repos.
|
||||
"git@git.kirsle.net:SketchyMaze/dpp": "dpp",
|
||||
}
|
||||
|
||||
# Software dependencies.
|
||||
dep_fedora = ["make", "golang", "SDL2-devel", "SDL2_ttf-devel", "SDL2_mixer-devel", "zip", "rsync"]
|
||||
|
@ -167,4 +171,8 @@ if __name__ == "__main__":
|
|||
https = k.replace("git@git.kirsle.net:", "https://git.kirsle.net/")
|
||||
repos[https] = repos[k]
|
||||
del repos[k]
|
||||
else:
|
||||
# mix in SSH-only repos
|
||||
repos.update(repos_ssh)
|
||||
|
||||
main()
|
||||
|
|
|
@ -155,7 +155,7 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
|
|||
if doodad.Title == "" {
|
||||
doodad.Title = "Converted Doodad"
|
||||
}
|
||||
doodad.Author = native.DefaultAuthor()
|
||||
doodad.Author = native.DefaultAuthor
|
||||
|
||||
// Write the first layer and gather its palette.
|
||||
log.Info("Converting first layer to drawing and getting the palette")
|
||||
|
@ -195,7 +195,7 @@ func imageToDrawing(c *cli.Context, chroma render.Color, inputFiles []string, ou
|
|||
if lvl.Title == "" {
|
||||
lvl.Title = "Converted Level"
|
||||
}
|
||||
lvl.Author = native.DefaultAuthor()
|
||||
lvl.Author = native.DefaultAuthor
|
||||
palette, chunker := imageToChunker(images[0], chroma, nil, lvl.Chunker.Size)
|
||||
lvl.Palette = palette
|
||||
lvl.Chunker = chunker
|
||||
|
|
|
@ -8,9 +8,9 @@ import (
|
|||
"time"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/cmd/doodad/commands"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding/builds"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/bootstrap"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
@ -27,19 +27,15 @@ func init() {
|
|||
}
|
||||
|
||||
func main() {
|
||||
bootstrap.InitPlugins()
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "doodad"
|
||||
app.Usage = "command line interface for Doodle"
|
||||
|
||||
var freeLabel string
|
||||
if !license.IsRegistered() {
|
||||
freeLabel = " (shareware)"
|
||||
}
|
||||
|
||||
app.Version = fmt.Sprintf("%s build %s%s. Built on %s",
|
||||
branding.Version,
|
||||
app.Version = fmt.Sprintf("%s build %s. Built on %s",
|
||||
builds.Version,
|
||||
Build,
|
||||
freeLabel,
|
||||
BuildDate,
|
||||
)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package command
|
||||
|
||||
import (
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/dpp/license"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/dpp/license"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/levelpack"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license/levelsigning"
|
||||
"git.kirsle.net/SketchyMaze/dpp/license"
|
||||
"git.kirsle.net/SketchyMaze/dpp/license/levelsigning"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/dpp/license"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ import (
|
|||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/levelpack"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license/levelsigning"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/dpp/license"
|
||||
"git.kirsle.net/SketchyMaze/dpp/license/levelsigning"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
|
|
|
@ -15,11 +15,13 @@ import (
|
|||
doodle "git.kirsle.net/SketchyMaze/doodle/pkg"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding/builds"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/chatbot"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/gamepad"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/bootstrap"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/sound"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/sprites"
|
||||
|
@ -51,15 +53,12 @@ func init() {
|
|||
func main() {
|
||||
runtime.LockOSThread()
|
||||
|
||||
bootstrap.InitPlugins()
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "doodle"
|
||||
app.Usage = fmt.Sprintf("%s - %s", branding.AppName, branding.Summary)
|
||||
|
||||
var freeLabel string
|
||||
if !license.IsRegistered() {
|
||||
freeLabel = " (shareware)"
|
||||
}
|
||||
|
||||
// Load user settings from disk ASAP.
|
||||
if err := usercfg.Load(); err != nil {
|
||||
log.Error("Error loading user settings (defaults will be used): %s", err)
|
||||
|
@ -73,10 +72,9 @@ func main() {
|
|||
// Set GameController style.
|
||||
gamepad.SetStyle(gamepad.Style(usercfg.Current.ControllerStyle))
|
||||
|
||||
app.Version = fmt.Sprintf("%s build %s%s. Built on %s",
|
||||
branding.Version,
|
||||
app.Version = fmt.Sprintf("%s build %s. Built on %s",
|
||||
builds.Version,
|
||||
Build,
|
||||
freeLabel,
|
||||
BuildDate,
|
||||
)
|
||||
|
||||
|
@ -119,6 +117,13 @@ func main() {
|
|||
}
|
||||
|
||||
app.Action = func(c *cli.Context) error {
|
||||
log.Info("Starting %s %s", app.Name, app.Version)
|
||||
|
||||
// Print registration information, + also this sets the DefaultAuthor field.
|
||||
if reg, err := dpp.Driver.GetRegistration(); err == nil {
|
||||
log.Info("Registered to %s", reg.Name)
|
||||
}
|
||||
|
||||
// --chdir into a different working directory? e.g. for Flatpak especially.
|
||||
if doodlePath := c.String("chdir"); doodlePath != "" {
|
||||
if err := os.Chdir(doodlePath); err != nil {
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
//go:build shareware
|
||||
// +build shareware
|
||||
|
||||
package balance
|
||||
|
||||
// FreeVersion is true in the free version of the game.
|
||||
const FreeVersion = true
|
|
@ -1,7 +0,0 @@
|
|||
//go:build !shareware
|
||||
// +build !shareware
|
||||
|
||||
package balance
|
||||
|
||||
// FreeVersion is true in the free version of the game.
|
||||
const FreeVersion = false
|
7
pkg/balance/tag_dpp.go
Normal file
7
pkg/balance/tag_dpp.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
//go:build dpp
|
||||
// +build dpp
|
||||
|
||||
package balance
|
||||
|
||||
// Doodle++ tag compiled in.
|
||||
const DPP = true
|
7
pkg/balance/tag_foss.go
Normal file
7
pkg/balance/tag_foss.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
//go:build !dpp
|
||||
// +build !dpp
|
||||
|
||||
package balance
|
||||
|
||||
// Doodle++ tag compiled in.
|
||||
const DPP = false
|
36
pkg/branding/builds/builds.go
Normal file
36
pkg/branding/builds/builds.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Package builds handles build-specific branding strings.
|
||||
package builds
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
)
|
||||
|
||||
var (
|
||||
/*
|
||||
Version string for user display.
|
||||
|
||||
It may look like the following:
|
||||
|
||||
- "v1.2.3 (open source)" for FOSS builds of the game.
|
||||
- "v1.2.3 (shareware)" for unregistered Doodle++ builds.
|
||||
- "v1.2.3" for registered Doodle++ builds.
|
||||
*/
|
||||
Version = branding.Version
|
||||
VersionSuffix = " (unknown)"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if !balance.DPP {
|
||||
VersionSuffix = " (open source)"
|
||||
} else if !dpp.Driver.IsRegistered() {
|
||||
VersionSuffix = " (shareware)"
|
||||
} else {
|
||||
VersionSuffix = " (registered)"
|
||||
}
|
||||
|
||||
Version = fmt.Sprintf("v%s%s", branding.Version, VersionSuffix)
|
||||
}
|
|
@ -9,11 +9,9 @@ import (
|
|||
"strings"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/assets"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/enum"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/filesystem"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/userdir"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/wasm"
|
||||
|
@ -114,27 +112,6 @@ func ListBuiltin() ([]string, error) {
|
|||
return result, nil
|
||||
}
|
||||
|
||||
/*
|
||||
LoadFromEmbeddable reads a doodad file, checking a level's embeddable
|
||||
file data in addition to the usual places.
|
||||
|
||||
Use a true value for `force` to always return the file if available. By
|
||||
default it will do a license check and free versions of the game won't
|
||||
read the asset and get an error instead. A "Signed Level" is allowed to
|
||||
use embedded assets in free versions and the caller uses force=true to
|
||||
communicate the signature status.
|
||||
*/
|
||||
func LoadFromEmbeddable(filename string, fs filesystem.Embeddable, force bool) (*Doodad, error) {
|
||||
if bin, err := fs.GetFile(balance.EmbeddedDoodadsBasePath + filename); err == nil {
|
||||
log.Debug("doodads.LoadFromEmbeddable: found %s", filename)
|
||||
if !force && !license.IsRegistered() {
|
||||
return nil, license.ErrRegisteredFeature
|
||||
}
|
||||
return Deserialize(filename, bin)
|
||||
}
|
||||
return LoadFile(filename)
|
||||
}
|
||||
|
||||
// LoadFile reads a doodad file from disk, checking a few locations.
|
||||
//
|
||||
// It checks for embedded bindata, system-level doodads on the filesystem,
|
||||
|
|
|
@ -15,16 +15,17 @@ import (
|
|||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level/giant_screenshot"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level/publishing"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/modal"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/modal/loadscreen"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/usercfg"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/userdir"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/windows"
|
||||
"git.kirsle.net/go/render"
|
||||
"git.kirsle.net/go/render/event"
|
||||
"git.kirsle.net/go/ui"
|
||||
)
|
||||
|
||||
// EditorScene manages the "Edit Level" game mode.
|
||||
|
@ -55,6 +56,8 @@ type EditorScene struct {
|
|||
filename string
|
||||
|
||||
lastAutosaveAt time.Time
|
||||
|
||||
winOpenLevel *ui.Window
|
||||
}
|
||||
|
||||
// Name of the scene.
|
||||
|
@ -252,7 +255,7 @@ func (s *EditorScene) setupAsync(d *Doodle) error {
|
|||
func (s *EditorScene) installActors() error {
|
||||
if err := s.UI.Canvas.InstallActors(s.Level.Actors); err != nil {
|
||||
summary := "This level references some doodads that were not found:"
|
||||
if strings.Contains(err.Error(), license.ErrRegisteredFeature.Error()) {
|
||||
if strings.Contains(err.Error(), plus.ErrRegisteredFeature.Error()) {
|
||||
summary = "This level contains embedded doodads, but this is not\n" +
|
||||
"available in the free version of the game. The following\n" +
|
||||
"doodads could not be loaded:"
|
||||
|
@ -507,7 +510,7 @@ func (s *EditorScene) LoadLevel(filename string) error {
|
|||
log.Info("Installing %d actors into the drawing", len(level.Actors))
|
||||
if err := s.UI.Canvas.InstallActors(level.Actors); err != nil {
|
||||
summary := "This level references some doodads that were not found:"
|
||||
if strings.Contains(err.Error(), license.ErrRegisteredFeature.Error()) {
|
||||
if strings.Contains(err.Error(), plus.ErrRegisteredFeature.Error()) {
|
||||
summary = "This level contains embedded doodads, but this is not\n" +
|
||||
"available in the free version of the game. The following\n" +
|
||||
"doodads could not be loaded:"
|
||||
|
@ -536,7 +539,7 @@ func (s *EditorScene) SaveLevel(filename string) error {
|
|||
m.Title = "Alpha"
|
||||
}
|
||||
if m.Author == "" {
|
||||
m.Author = native.DefaultAuthor()
|
||||
m.Author = native.DefaultAuthor
|
||||
}
|
||||
|
||||
m.Palette = s.UI.Canvas.Palette
|
||||
|
@ -638,7 +641,7 @@ func (s *EditorScene) SaveDoodad(filename string) error {
|
|||
d.Title = "Untitled Doodad"
|
||||
}
|
||||
if d.Author == "" {
|
||||
d.Author = native.DefaultAuthor()
|
||||
d.Author = native.DefaultAuthor
|
||||
}
|
||||
|
||||
// TODO: is this copying necessary?
|
||||
|
|
|
@ -6,11 +6,11 @@ import (
|
|||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding/builds"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/drawtool"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/enum"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/uix"
|
||||
|
@ -595,12 +595,8 @@ func (u *EditorUI) SetupStatusBar(d *Doodle) *ui.Frame {
|
|||
}
|
||||
}
|
||||
|
||||
var shareware string
|
||||
if !license.IsRegistered() {
|
||||
shareware = " (shareware)"
|
||||
}
|
||||
extraLabel := ui.NewLabel(ui.Label{
|
||||
Text: fmt.Sprintf("%s v%s%s", branding.AppName, branding.Version, shareware),
|
||||
Text: fmt.Sprintf("%s %s", branding.AppName, builds.Version),
|
||||
Font: balance.StatusFont,
|
||||
})
|
||||
extraLabel.Configure(ui.Config{
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/uix"
|
||||
"git.kirsle.net/go/render"
|
||||
)
|
||||
|
@ -34,7 +35,7 @@ func (u *EditorUI) startDragActor(doodad *doodads.Doodad, actor *level.Actor) {
|
|||
|
||||
if doodad == nil {
|
||||
if actor != nil {
|
||||
obj, err := doodads.LoadFromEmbeddable(actor.Filename, u.Scene.Level, false)
|
||||
obj, err := dpp.Driver.LoadFromEmbeddable(actor.Filename, u.Scene.Level, false)
|
||||
if err != nil {
|
||||
log.Error("startDragExistingActor: actor doodad name %s not found: %s", actor.Filename, err)
|
||||
return
|
||||
|
|
|
@ -9,10 +9,11 @@ import (
|
|||
"git.kirsle.net/SketchyMaze/doodle/pkg/drawtool"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/enum"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level/giant_screenshot"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/modal"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/userdir"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/windows"
|
||||
"git.kirsle.net/go/render"
|
||||
|
@ -111,9 +112,11 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
levelMenu.AddItemAccel("Playtest", "P", func() {
|
||||
u.Scene.Playtest()
|
||||
})
|
||||
if balance.DPP {
|
||||
levelMenu.AddItem("Publish", func() {
|
||||
u.OpenPublishWindow()
|
||||
})
|
||||
}
|
||||
|
||||
levelMenu.AddSeparator()
|
||||
levelMenu.AddItem("Screenshot", func() {
|
||||
|
@ -277,16 +280,21 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
// Help menu
|
||||
var (
|
||||
helpMenu = u.d.MakeHelpMenu(menu, u.Supervisor)
|
||||
registerText = "Register"
|
||||
)
|
||||
helpMenu.AddSeparator()
|
||||
if license.IsRegistered() {
|
||||
|
||||
// Registration item for Doodle++ builds.
|
||||
if balance.DPP {
|
||||
var registerText = "Register"
|
||||
if dpp.Driver.IsRegistered() {
|
||||
registerText = "Registration"
|
||||
}
|
||||
|
||||
helpMenu.AddSeparator()
|
||||
helpMenu.AddItem(registerText, func() {
|
||||
u.licenseWindow.Show()
|
||||
u.Supervisor.FocusWindow(u.licenseWindow)
|
||||
})
|
||||
}
|
||||
|
||||
menu.Supervise(u.Supervisor)
|
||||
menu.Compute(d.Engine)
|
||||
|
@ -313,7 +321,24 @@ func (s *EditorScene) MenuNewDoodad() {
|
|||
// File->Open, or Ctrl-O
|
||||
func (s *EditorScene) MenuOpen() {
|
||||
s.ConfirmUnload(func() {
|
||||
s.d.GotoLoadMenu()
|
||||
if s.winOpenLevel == nil {
|
||||
s.winOpenLevel = windows.NewOpenDrawingWindow(windows.OpenDrawing{
|
||||
Supervisor: s.UI.Supervisor,
|
||||
Engine: shmem.CurrentRenderEngine,
|
||||
OnOpenDrawing: func(filename string) {
|
||||
s.d.EditFile(filename)
|
||||
},
|
||||
OnCloseWindow: func() {
|
||||
s.winOpenLevel.Destroy()
|
||||
s.winOpenLevel = nil
|
||||
},
|
||||
})
|
||||
}
|
||||
s.winOpenLevel.MoveTo(render.Point{
|
||||
X: (s.d.width / 2) - (s.winOpenLevel.Size().W / 2),
|
||||
Y: (s.d.height / 2) - (s.winOpenLevel.Size().H / 2),
|
||||
})
|
||||
s.winOpenLevel.Show()
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -9,9 +9,9 @@ import (
|
|||
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/drawtool"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/modal"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/windows"
|
||||
"git.kirsle.net/go/render"
|
||||
"git.kirsle.net/go/ui"
|
||||
|
@ -68,7 +68,7 @@ func (u *EditorUI) OpenPublishWindow() {
|
|||
OnPublish: func(includeBuiltins bool) {
|
||||
u.d.FlashError("OnPublish Called")
|
||||
// XXX: Paid Version Only.
|
||||
if !license.IsRegistered() {
|
||||
if !dpp.Driver.IsRegistered() {
|
||||
if u.licenseWindow != nil {
|
||||
u.licenseWindow.Show()
|
||||
u.Supervisor.FocusWindow(u.licenseWindow)
|
||||
|
|
|
@ -9,9 +9,9 @@ import (
|
|||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/userdir"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/wallpaper"
|
||||
|
@ -95,7 +95,7 @@ func GiantScreenshot(lvl *level.Level) (image.Image, error) {
|
|||
// Render the doodads.
|
||||
log.Debug("GiantScreenshot: Render actors...")
|
||||
for _, actor := range lvl.Actors {
|
||||
doodad, err := doodads.LoadFromEmbeddable(actor.Filename, lvl, false)
|
||||
doodad, err := dpp.Driver.LoadFromEmbeddable(actor.Filename, lvl, false)
|
||||
if err != nil {
|
||||
log.Error("GiantScreenshot: Load doodad: %s", err)
|
||||
continue
|
||||
|
|
|
@ -12,9 +12,9 @@ import (
|
|||
"time"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/userdir"
|
||||
"git.kirsle.net/go/render"
|
||||
"golang.org/x/image/draw"
|
||||
|
@ -78,7 +78,7 @@ func CroppedScreenshot(lvl *level.Level, viewport render.Rect) (image.Image, err
|
|||
// Render the doodads.
|
||||
log.Debug("CroppedScreenshot: Render actors...")
|
||||
for _, actor := range lvl.Actors {
|
||||
doodad, err := doodads.LoadFromEmbeddable(actor.Filename, lvl, false)
|
||||
doodad, err := dpp.Driver.LoadFromEmbeddable(actor.Filename, lvl, false)
|
||||
if err != nil {
|
||||
log.Error("CroppedScreenshot: Load doodad: %s", err)
|
||||
continue
|
||||
|
|
|
@ -17,8 +17,8 @@ import (
|
|||
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -37,7 +37,7 @@ func Publish(lvl *level.Level) error {
|
|||
}
|
||||
|
||||
// Registered games only.
|
||||
if !license.IsRegistered() {
|
||||
if !balance.DPP || !dpp.Driver.IsRegistered() {
|
||||
return errors.New("only registered versions of the game can attach doodads to levels")
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ func Publish(lvl *level.Level) error {
|
|||
log.Debug("Embed filename: %s", filename)
|
||||
names[filename] = nil
|
||||
|
||||
doodad, err := doodads.LoadFromEmbeddable(filename, lvl, false)
|
||||
doodad, err := dpp.Driver.LoadFromEmbeddable(filename, lvl, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't load doodad %s: %s", filename, err)
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ func New() *Level {
|
|||
Base: Base{
|
||||
Version: 1,
|
||||
Title: "Untitled",
|
||||
Author: native.DefaultAuthor(),
|
||||
Author: native.DefaultAuthor,
|
||||
Files: NewFileSystem(),
|
||||
},
|
||||
Chunker: NewChunker(balance.ChunkSize),
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
package license
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
// AdminGenerateKeys generates the ECDSA public and private key pair for the admin
|
||||
// side of creating signed license files.
|
||||
func AdminGenerateKeys() (*ecdsa.PrivateKey, error) {
|
||||
privateKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
return privateKey, err
|
||||
}
|
||||
|
||||
// AdminWriteKeys writes the admin signing key to .pem files on disk.
|
||||
func AdminWriteKeys(key *ecdsa.PrivateKey, privateFile, publicFile string) error {
|
||||
// Encode the private key to PEM format.
|
||||
x509Encoded, err := x509.MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pemEncoded := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: x509Encoded,
|
||||
})
|
||||
|
||||
// Encode the public key to PEM format.
|
||||
x509EncodedPub, err := x509.MarshalPKIXPublicKey(key.Public())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pemEncodedPub := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PUBLIC KEY",
|
||||
Bytes: x509EncodedPub,
|
||||
})
|
||||
|
||||
// Write the files.
|
||||
if err := ioutil.WriteFile(privateFile, pemEncoded, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(publicFile, pemEncodedPub, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AdminLoadPrivateKey loads the private key from disk.
|
||||
func AdminLoadPrivateKey(privateFile string) (*ecdsa.PrivateKey, error) {
|
||||
// Read the private key file.
|
||||
pemEncoded, err := ioutil.ReadFile(privateFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Decode the private key.
|
||||
block, _ := pem.Decode([]byte(pemEncoded))
|
||||
x509Encoded := block.Bytes
|
||||
privateKey, _ := x509.ParseECPrivateKey(x509Encoded)
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
// AdminLoadPublicKey loads the private key from disk.
|
||||
func AdminLoadPublicKey(publicFile string) (*ecdsa.PublicKey, error) {
|
||||
pemEncodedPub, err := ioutil.ReadFile(publicFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Decode the public key.
|
||||
blockPub, _ := pem.Decode([]byte(pemEncodedPub))
|
||||
x509EncodedPub := blockPub.Bytes
|
||||
genericPublicKey, _ := x509.ParsePKIXPublicKey(x509EncodedPub)
|
||||
publicKey := genericPublicKey.(*ecdsa.PublicKey)
|
||||
|
||||
return publicKey, nil
|
||||
}
|
||||
|
||||
// AdminSignRegistration signs the registration object.
|
||||
func AdminSignRegistration(key *ecdsa.PrivateKey, reg Registration) (string, error) {
|
||||
reg.StandardClaims = jwt.StandardClaims{
|
||||
Issuer: "Maze Admin",
|
||||
IssuedAt: time.Now().Unix(),
|
||||
NotBefore: time.Now().Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodES384, reg)
|
||||
signed, err := token.SignedString(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return signed, nil
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package license
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Run-time configuration variables provided by the application.
|
||||
const pemSigningKey = `-----BEGIN PUBLIC KEY-----
|
||||
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEMrqAMHjZ1dPlKwDOsiCSr5N3OSvnYKLM
|
||||
efe2xD+5hJYrpvparRFnaMbMuqde4M6d6sCCKO8BHtfAzmyiQ/CD38zs9MiDsamy
|
||||
FDYEEJu+Fqx482I7fIa5ZEE770+wWJ3k
|
||||
-----END PUBLIC KEY-----`
|
||||
|
||||
var Signer *ecdsa.PublicKey
|
||||
|
||||
func init() {
|
||||
key, err := ParsePublicKeyPEM(pemSigningKey)
|
||||
if err != nil {
|
||||
fmt.Printf("license: failed to parse app keys: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
Signer = key
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
package levelsigning
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/levelpack"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
)
|
||||
|
||||
// IsLevelSigned returns a quick answer.
|
||||
func IsLevelSigned(lvl *level.Level) bool {
|
||||
return VerifyLevel(license.Signer, lvl)
|
||||
}
|
||||
|
||||
// IsLevelPackSigned returns a quick answer.
|
||||
func IsLevelPackSigned(lp *levelpack.LevelPack) bool {
|
||||
return VerifyLevelPack(license.Signer, lp)
|
||||
}
|
||||
|
||||
/*
|
||||
SignLevel creates a signature on a level file which allows it to load its
|
||||
embedded doodads even for free versions of the game.
|
||||
|
||||
Free versions will verify a level's signature before bailing out with the
|
||||
"can't play levels w/ embedded doodads" response.
|
||||
|
||||
NOTE: this only supported Zipfile levels and will assume the level you
|
||||
pass has a Zipfile to access embedded assets.
|
||||
*/
|
||||
func SignLevel(key *ecdsa.PrivateKey, lvl *level.Level) ([]byte, error) {
|
||||
// Encode the attached files data to deterministic JSON.
|
||||
certificate, err := StringifyAssets(lvl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("Sign file tree: %s", certificate)
|
||||
digest := shasum(certificate)
|
||||
|
||||
signature, err := ecdsa.SignASN1(rand.Reader, key, digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("Digest: %x Signature: %x", digest, signature)
|
||||
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
// VerifyLevel verifies a level's signature and returns if it is OK.
|
||||
func VerifyLevel(publicKey *ecdsa.PublicKey, lvl *level.Level) bool {
|
||||
// No signature = not verified.
|
||||
if lvl.Signature == nil || len(lvl.Signature) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Encode the attached files data to deterministic JSON.
|
||||
certificate, err := StringifyAssets(lvl)
|
||||
if err != nil {
|
||||
log.Error("VerifyLevel: couldn't stringify assets: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
digest := shasum(certificate)
|
||||
|
||||
// Verify the signature against our public key.
|
||||
return ecdsa.VerifyASN1(publicKey, digest, lvl.Signature)
|
||||
}
|
||||
|
||||
/*
|
||||
SignLevelpack applies a signature to a levelpack as a whole, to allow its
|
||||
shared custom doodads to be loaded by its levels in free games.
|
||||
*/
|
||||
func SignLevelPack(key *ecdsa.PrivateKey, lp *levelpack.LevelPack) ([]byte, error) {
|
||||
// Encode the attached files data to deterministic JSON.
|
||||
certificate, err := StringifyLevelpackAssets(lp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("Sign file tree: %s", certificate)
|
||||
digest := shasum(certificate)
|
||||
|
||||
signature, err := ecdsa.SignASN1(rand.Reader, key, digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("Digest: %x Signature: %x", digest, signature)
|
||||
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
// VerifyLevelPack verifies a levelpack's signature and returns if it is OK.
|
||||
func VerifyLevelPack(publicKey *ecdsa.PublicKey, lp *levelpack.LevelPack) bool {
|
||||
// No signature = not verified.
|
||||
if lp.Signature == nil || len(lp.Signature) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Encode the attached files data to deterministic JSON.
|
||||
certificate, err := StringifyLevelpackAssets(lp)
|
||||
if err != nil {
|
||||
log.Error("VerifyLevelPack: couldn't stringify assets: %s", err)
|
||||
return false
|
||||
}
|
||||
|
||||
digest := shasum(certificate)
|
||||
|
||||
// Verify the signature against our public key.
|
||||
return ecdsa.VerifyASN1(publicKey, digest, lp.Signature)
|
||||
}
|
||||
|
||||
// StringifyAssets creates the signing checksum of a level's attached assets.
|
||||
func StringifyAssets(lvl *level.Level) ([]byte, error) {
|
||||
// Get a listing of all embedded files. Note: gives us a conveniently
|
||||
// sorted array of files too.
|
||||
files := lvl.Files.List()
|
||||
|
||||
// Pair each filename with its SHA256 sum.
|
||||
var checksum = map[string]string{}
|
||||
for _, filename := range files {
|
||||
if sum, err := lvl.Files.Checksum(filename); err != nil {
|
||||
return nil, fmt.Errorf("when checksum %s got error: %s", filename, err)
|
||||
} else {
|
||||
checksum[filename] = sum
|
||||
}
|
||||
}
|
||||
|
||||
// Encode the payload to deterministic JSON.
|
||||
certificate, err := json.Marshal(checksum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return certificate, nil
|
||||
}
|
||||
|
||||
// StringifyLevelpackAssets creates the signing checksum of a level's attached assets.
|
||||
func StringifyLevelpackAssets(lp *levelpack.LevelPack) ([]byte, error) {
|
||||
var (
|
||||
files = []string{}
|
||||
seen = map[string]struct{}{}
|
||||
)
|
||||
|
||||
// Enumerate the files in the zipfile assets/ folder.
|
||||
for _, file := range lp.Zipfile.File {
|
||||
if file.Name == "index.json" {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := seen[file.Name]; !ok {
|
||||
files = append(files, file.Name)
|
||||
seen[file.Name] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// Pair each filename with its SHA256 sum.
|
||||
var checksum = map[string]string{}
|
||||
for _, filename := range files {
|
||||
file, err := lp.Zipfile.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bin, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
checksum[filename] = fmt.Sprintf("%x", shasum(bin))
|
||||
}
|
||||
|
||||
// Encode the payload to deterministic JSON.
|
||||
certificate, err := json.Marshal(checksum)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return certificate, nil
|
||||
}
|
||||
|
||||
// Common function to SHA-256 checksum a thing.
|
||||
func shasum(data []byte) []byte {
|
||||
h := sha256.New()
|
||||
h.Write(data)
|
||||
return h.Sum(nil)
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
// Package license holds functions related to paid product activation.
|
||||
package license
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/userdir"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrRegisteredFeature = errors.New("feature not available")
|
||||
)
|
||||
|
||||
// Registration object encoded into a license key file.
|
||||
type Registration struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
jwt.StandardClaims
|
||||
}
|
||||
|
||||
// IsRegistered returns a boolean answer: is the product registered?
|
||||
func IsRegistered() bool {
|
||||
if _, err := GetRegistration(); err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetRegistration returns the currently registered user, by checking
|
||||
// for the license.key file in the profile folder.
|
||||
func GetRegistration() (Registration, error) {
|
||||
if Signer == nil {
|
||||
return Registration{}, errors.New("signer not ready")
|
||||
}
|
||||
|
||||
filename := filepath.Join(userdir.ProfileDirectory, "license.key")
|
||||
jwt, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return Registration{}, err
|
||||
}
|
||||
|
||||
// Check if the JWT is valid.
|
||||
reg, err := Validate(Signer, string(jwt))
|
||||
if err != nil {
|
||||
return Registration{}, err
|
||||
}
|
||||
|
||||
return reg, err
|
||||
}
|
||||
|
||||
// UploadLicenseFile handles the user selecting the license key file, and it is
|
||||
// validated and ingested.
|
||||
func UploadLicenseFile(filename string) (Registration, error) {
|
||||
if Signer == nil {
|
||||
return Registration{}, errors.New("signer not ready")
|
||||
}
|
||||
|
||||
jwt, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return Registration{}, err
|
||||
}
|
||||
|
||||
// Check if the JWT is valid.
|
||||
reg, err := Validate(Signer, string(jwt))
|
||||
if err != nil {
|
||||
return Registration{}, err
|
||||
}
|
||||
|
||||
// Upload the license to Doodle's profile directory.
|
||||
outfile := filepath.Join(userdir.ProfileDirectory, "license.key")
|
||||
if err := ioutil.WriteFile(outfile, jwt, 0644); err != nil {
|
||||
return Registration{}, err
|
||||
}
|
||||
|
||||
return reg, nil
|
||||
}
|
||||
|
||||
// Validate the registration is signed by the appropriate public key.
|
||||
func Validate(publicKey *ecdsa.PublicKey, tokenString string) (Registration, error) {
|
||||
var reg Registration
|
||||
token, err := jwt.ParseWithClaims(tokenString, ®, func(token *jwt.Token) (interface{}, error) {
|
||||
return publicKey, nil
|
||||
})
|
||||
if err != nil {
|
||||
return reg, err
|
||||
}
|
||||
|
||||
if !token.Valid {
|
||||
return reg, errors.New("token not valid")
|
||||
}
|
||||
return reg, nil
|
||||
}
|
||||
|
||||
// ParsePublicKeyPEM loads a public key from PEM format.
|
||||
func ParsePublicKeyPEM(keytext string) (*ecdsa.PublicKey, error) {
|
||||
blockPub, _ := pem.Decode([]byte(keytext))
|
||||
x509EncodedPub := blockPub.Bytes
|
||||
genericPublicKey, _ := x509.ParsePKIXPublicKey(x509EncodedPub)
|
||||
publicKey := genericPublicKey.(*ecdsa.PublicKey)
|
||||
return publicKey, nil
|
||||
}
|
|
@ -6,12 +6,13 @@ import (
|
|||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding/builds"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/levelpack"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/modal/loadscreen"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/savegame"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/scripting"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
|
||||
|
@ -120,12 +121,8 @@ func (s *MainScene) Setup(d *Doodle) error {
|
|||
s.labelSubtitle.Compute(d.Engine)
|
||||
|
||||
// Version label.
|
||||
var shareware string
|
||||
if !license.IsRegistered() {
|
||||
shareware = " (shareware)"
|
||||
}
|
||||
ver := ui.NewLabel(ui.Label{
|
||||
Text: fmt.Sprintf("v%s%s", branding.Version, shareware),
|
||||
Text: builds.Version,
|
||||
Font: balance.TitleScreenVersionFont,
|
||||
})
|
||||
ver.Compute(d.Engine)
|
||||
|
@ -228,7 +225,7 @@ func (s *MainScene) Setup(d *Doodle) error {
|
|||
{
|
||||
Name: "Register",
|
||||
If: func() bool {
|
||||
return !license.IsRegistered()
|
||||
return balance.DPP && !dpp.Driver.IsRegistered()
|
||||
},
|
||||
Func: func() {
|
||||
if s.winRegister == nil {
|
||||
|
|
|
@ -73,6 +73,8 @@ func (d *Doodle) GotoNewDoodadMenu() {
|
|||
}
|
||||
|
||||
// GotoLoadMenu loads the MenuScene and shows the "Load" window.
|
||||
//
|
||||
// DEPRECATED: loads the old menu, in dev console run `$ d.GotoLoadMenu()` to see.
|
||||
func (d *Doodle) GotoLoadMenu() {
|
||||
log.Info("Loading the MenuScene to the Load window for Edit Mode")
|
||||
scene := &MenuScene{
|
||||
|
@ -83,6 +85,8 @@ func (d *Doodle) GotoLoadMenu() {
|
|||
|
||||
// GotoPlayMenu loads the MenuScene and shows the "Load" window for playing a
|
||||
// level, not editing it.
|
||||
//
|
||||
// DEPRECATED: loads the old menu, in dev console run `$ d.GotoPlayMenu()` to see.
|
||||
func (d *Doodle) GotoPlayMenu() {
|
||||
log.Info("Loading the MenuScene to the Load window for Play Mode")
|
||||
scene := &MenuScene{
|
||||
|
|
|
@ -2,28 +2,9 @@ package native
|
|||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
)
|
||||
|
||||
var USER string = os.Getenv("USER")
|
||||
|
||||
/*
|
||||
DefaultAuthor will return the local user's name to be the default Author
|
||||
for levels and doodads they create.
|
||||
|
||||
If they have registered the game, use the name from their license JWT token.
|
||||
|
||||
Otherwise fall back to their native operating system user.
|
||||
*/
|
||||
func DefaultAuthor() string {
|
||||
// Are we registered?
|
||||
if license.IsRegistered() {
|
||||
if reg, err := license.GetRegistration(); err == nil {
|
||||
return reg.Name
|
||||
}
|
||||
}
|
||||
|
||||
// Return OS username
|
||||
return os.Getenv("USER")
|
||||
}
|
||||
var (
|
||||
USER string = os.Getenv("USER")
|
||||
DefaultAuthor = USER
|
||||
)
|
||||
|
|
|
@ -13,12 +13,12 @@ import (
|
|||
"git.kirsle.net/SketchyMaze/doodle/pkg/keybind"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/levelpack"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license/levelsigning"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/modal"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/modal/loadscreen"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/physics"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/savegame"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/scripting"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/shmem"
|
||||
|
@ -62,6 +62,7 @@ type PlayScene struct {
|
|||
menubar *ui.MenuBar
|
||||
editButton *ui.Button
|
||||
winLevelPacks *ui.Window
|
||||
winOpenLevel *ui.Window
|
||||
|
||||
// Custom debug labels.
|
||||
debPosition *string
|
||||
|
@ -246,7 +247,7 @@ func (s *PlayScene) setupAsync(d *Doodle) error {
|
|||
s.drawing.OnResetTimer = s.ResetTimer
|
||||
|
||||
// If this level game from a signed LevelPack, inform the canvas.
|
||||
if s.LevelPack != nil && levelsigning.IsLevelPackSigned(s.LevelPack) {
|
||||
if s.LevelPack != nil && dpp.Driver.IsLevelPackSigned(s.LevelPack) {
|
||||
s.drawing.IsSignedLevelPack = s.LevelPack
|
||||
}
|
||||
|
||||
|
@ -336,7 +337,7 @@ func (s *PlayScene) setupAsync(d *Doodle) error {
|
|||
func (s *PlayScene) installActors() error {
|
||||
if err := s.drawing.InstallActors(s.Level.Actors); err != nil {
|
||||
summary := "This level references some doodads that were not found:"
|
||||
if strings.Contains(err.Error(), license.ErrRegisteredFeature.Error()) {
|
||||
if strings.Contains(err.Error(), plus.ErrRegisteredFeature.Error()) {
|
||||
summary = "This level contains embedded doodads, but this is not\n" +
|
||||
"available in the free version of the game. The following\n" +
|
||||
"doodads could not be loaded:"
|
||||
|
@ -503,7 +504,7 @@ func (s *PlayScene) setupPlayer(playerCharacterFilename string) {
|
|||
// centerIn is optional, ignored if zero.
|
||||
func (s *PlayScene) installPlayerDoodad(filename string, spawn render.Point, centerIn render.Rect) {
|
||||
// Load in the player character.
|
||||
player, err := doodads.LoadFromEmbeddable(filename, s.Level, false)
|
||||
player, err := dpp.Driver.LoadFromEmbeddable(filename, s.Level, false)
|
||||
if err != nil {
|
||||
log.Error("PlayScene.Setup: failed to load player doodad: %s", err)
|
||||
player = doodads.NewDummy(32)
|
||||
|
|
|
@ -40,7 +40,26 @@ func (u *PlayScene) setupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
u.winLevelPacks.Show()
|
||||
})
|
||||
gameMenu.AddItemAccel("New drawing", "Ctrl-N", d.GotoNewMenu)
|
||||
gameMenu.AddItemAccel("Open drawing", "Ctrl-O", d.GotoLoadMenu)
|
||||
gameMenu.AddItemAccel("Open drawing", "Ctrl-O", func() {
|
||||
if u.winOpenLevel == nil {
|
||||
u.winOpenLevel = windows.NewOpenDrawingWindow(windows.OpenDrawing{
|
||||
Supervisor: u.Supervisor,
|
||||
Engine: shmem.CurrentRenderEngine,
|
||||
OnOpenDrawing: func(filename string) {
|
||||
d.EditFile(filename)
|
||||
},
|
||||
OnCloseWindow: func() {
|
||||
u.winOpenLevel.Destroy()
|
||||
u.winOpenLevel = nil
|
||||
},
|
||||
})
|
||||
}
|
||||
u.winOpenLevel.MoveTo(render.Point{
|
||||
X: (d.width / 2) - (u.winOpenLevel.Size().W / 2),
|
||||
Y: (d.height / 2) - (u.winOpenLevel.Size().H / 2),
|
||||
})
|
||||
u.winOpenLevel.Show()
|
||||
})
|
||||
|
||||
gameMenu.AddSeparator()
|
||||
gameMenu.AddItem("Quit to menu", func() {
|
||||
|
|
18
pkg/plus/bootstrap/bootstrap.go
Normal file
18
pkg/plus/bootstrap/bootstrap.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
Package bootstrap is a common import between the Doodle and Doodad programs.
|
||||
|
||||
Its chief job is to work around circular dependency issues when dealing with
|
||||
pluggable parts of the codebase, such as Doodle++ which adds features for the
|
||||
official release which are missing from the FOSS version.
|
||||
*/
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
)
|
||||
|
||||
var Driver dpp.Pluggable
|
||||
|
||||
func InitPlugins() {
|
||||
Driver = dpp.Plugin{}
|
||||
}
|
24
pkg/plus/dpp/plugin.go
Normal file
24
pkg/plus/dpp/plugin.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package dpp
|
||||
|
||||
import (
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/filesystem"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/levelpack"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus"
|
||||
)
|
||||
|
||||
// Driver is the currently installed Doodle++ implementation (FOSS or DPP).
|
||||
var Driver Pluggable
|
||||
|
||||
// Pluggable defines the interface for Doodle++ functions, so that their implementations
|
||||
// can avoid cyclic dependency errors. Documentation for these functions is only spelled
|
||||
// out in the SketchyMaze/dpp package.
|
||||
type Pluggable interface {
|
||||
LoadFromEmbeddable(string, filesystem.Embeddable, bool) (*doodads.Doodad, error)
|
||||
IsRegistered() bool
|
||||
GetRegistration() (plus.Registration, error)
|
||||
UploadLicenseFile(string) (plus.Registration, error)
|
||||
IsLevelSigned(*level.Level) bool
|
||||
IsLevelPackSigned(*levelpack.LevelPack) bool
|
||||
}
|
77
pkg/plus/dpp/plus_dpp.go
Normal file
77
pkg/plus/dpp/plus_dpp.go
Normal file
|
@ -0,0 +1,77 @@
|
|||
//go:build dpp
|
||||
// +build dpp
|
||||
|
||||
package dpp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/filesystem"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/levelpack"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus"
|
||||
"git.kirsle.net/SketchyMaze/dpp/embedding"
|
||||
"git.kirsle.net/SketchyMaze/dpp/license"
|
||||
"git.kirsle.net/SketchyMaze/dpp/license/levelsigning"
|
||||
)
|
||||
|
||||
type Plugin struct{}
|
||||
|
||||
func (Plugin) LoadFromEmbeddable(filename string, fs filesystem.Embeddable, force bool) (*doodads.Doodad, error) {
|
||||
return embedding.LoadFromEmbeddable(filename, fs, force)
|
||||
}
|
||||
|
||||
func (Plugin) IsRegistered() bool {
|
||||
return license.IsRegistered()
|
||||
}
|
||||
|
||||
func (Plugin) GetRegistration() (plus.Registration, error) {
|
||||
reg, err := license.GetRegistration()
|
||||
if err != nil {
|
||||
return plus.Registration{}, err
|
||||
}
|
||||
|
||||
return translateLicenseStruct(reg)
|
||||
}
|
||||
|
||||
func (Plugin) UploadLicenseFile(filename string) (plus.Registration, error) {
|
||||
reg, err := license.UploadLicenseFile(filename)
|
||||
if err != nil {
|
||||
return plus.Registration{}, err
|
||||
}
|
||||
|
||||
return translateLicenseStruct(reg)
|
||||
}
|
||||
|
||||
// Hack: to translate JWT token types, easiest is to just encode/decode them (inner jwt.StandardClaims complexity).
|
||||
func translateLicenseStruct(reg license.Registration) (plus.Registration, error) {
|
||||
// Set the DefaultAuthor to the registered user's name.
|
||||
if reg.Name != "" {
|
||||
native.DefaultAuthor = reg.Name
|
||||
}
|
||||
|
||||
// Marshal to JSON and back to cast the type.
|
||||
var (
|
||||
result plus.Registration
|
||||
jsonStr, err = json.Marshal(reg)
|
||||
)
|
||||
if err != nil {
|
||||
return plus.Registration{}, err
|
||||
}
|
||||
err = json.Unmarshal(jsonStr, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (Plugin) IsLevelPackSigned(lp *levelpack.LevelPack) bool {
|
||||
return levelsigning.IsLevelPackSigned(lp)
|
||||
}
|
||||
|
||||
func (Plugin) IsLevelSigned(lvl *level.Level) bool {
|
||||
return levelsigning.IsLevelSigned(lvl)
|
||||
}
|
||||
|
||||
func init() {
|
||||
Driver = Plugin{}
|
||||
}
|
46
pkg/plus/dpp/plus_foss.go
Normal file
46
pkg/plus/dpp/plus_foss.go
Normal file
|
@ -0,0 +1,46 @@
|
|||
//go:build !dpp
|
||||
// +build !dpp
|
||||
|
||||
package dpp
|
||||
|
||||
import (
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/filesystem"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/levelpack"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus"
|
||||
)
|
||||
|
||||
type Plugin struct{}
|
||||
|
||||
func (Plugin) LoadFromEmbeddable(filename string, fs filesystem.Embeddable, force bool) (*doodads.Doodad, error) {
|
||||
if result, err := doodads.LoadFile(filename); err != nil {
|
||||
return nil, plus.ErrRegisteredFeature
|
||||
} else {
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (Plugin) IsRegistered() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (Plugin) GetRegistration() (plus.Registration, error) {
|
||||
return plus.Registration{}, plus.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (Plugin) UploadLicenseFile(string) (plus.Registration, error) {
|
||||
return plus.Registration{}, plus.ErrNotImplemented
|
||||
}
|
||||
|
||||
func (Plugin) IsLevelPackSigned(*levelpack.LevelPack) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (Plugin) IsLevelSigned(*level.Level) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func init() {
|
||||
Driver = Plugin{}
|
||||
}
|
21
pkg/plus/plus.go
Normal file
21
pkg/plus/plus.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Package plus connects the open source Doodle engine to the Doodle++ feature.
|
||||
package plus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrNotImplemented = errors.New("not implemented")
|
||||
ErrRegisteredFeature = errors.New("feature not available")
|
||||
)
|
||||
|
||||
// Registration object encoded into a license key file.
|
||||
type Registration struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
jwt.StandardClaims
|
||||
}
|
|
@ -6,10 +6,9 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/doodads"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/level"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license/levelsigning"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/scripting"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/scripting/exceptions"
|
||||
"git.kirsle.net/go/render"
|
||||
|
@ -37,18 +36,18 @@ func (w *Canvas) InstallActors(actors level.ActorMap) error {
|
|||
// Signed Levels: the free version normally won't load embedded assets from
|
||||
// a level and the call to LoadFromEmbeddable below returns the error. If the
|
||||
// level is signed it is allowed to use its embedded assets.
|
||||
isSigned := w.IsSignedLevelPack != nil || levelsigning.IsLevelSigned(w.level)
|
||||
isSigned := w.IsSignedLevelPack != nil || dpp.Driver.IsLevelSigned(w.level)
|
||||
|
||||
w.actors = make([]*Actor, 0)
|
||||
for _, id := range actorIDs {
|
||||
var actor = actors[id]
|
||||
|
||||
// Try loading the doodad from the level's own attached files.
|
||||
doodad, err := doodads.LoadFromEmbeddable(actor.Filename, w.level, isSigned)
|
||||
doodad, err := dpp.Driver.LoadFromEmbeddable(actor.Filename, w.level, isSigned)
|
||||
if err != nil {
|
||||
// If we have a signed levelpack, try loading from the levelpack.
|
||||
if w.IsSignedLevelPack != nil {
|
||||
if found, err := doodads.LoadFromEmbeddable(actor.Filename, w.IsSignedLevelPack, true); err == nil {
|
||||
if found, err := dpp.Driver.LoadFromEmbeddable(actor.Filename, w.IsSignedLevelPack, true); err == nil {
|
||||
doodad = found
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@ import (
|
|||
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/balance"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/license"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/branding/builds"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/log"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/modal"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/native"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus"
|
||||
"git.kirsle.net/SketchyMaze/doodle/pkg/plus/dpp"
|
||||
"git.kirsle.net/go/render"
|
||||
"git.kirsle.net/go/ui"
|
||||
"git.kirsle.net/go/ui/style"
|
||||
|
@ -50,12 +52,12 @@ func NewLicenseWindow(cfg License) *ui.Window {
|
|||
labelSize = render.NewRect(100, 16)
|
||||
valueSize = render.NewRect(windowWidth-labelSize.W-4, labelSize.H)
|
||||
isRegistered bool
|
||||
registration license.Registration
|
||||
summary = "Unregistered (shareware)"
|
||||
registration plus.Registration
|
||||
summary = "Unregistered" + builds.VersionSuffix
|
||||
)
|
||||
|
||||
// Get our current registration status.
|
||||
if reg, err := license.GetRegistration(); err == nil {
|
||||
if reg, err := dpp.Driver.GetRegistration(); err == nil {
|
||||
isRegistered = true
|
||||
registration = reg
|
||||
windowHeight = 200
|
||||
|
@ -140,7 +142,7 @@ func NewLicenseWindow(cfg License) *ui.Window {
|
|||
}
|
||||
|
||||
// Upload and validate the license key.
|
||||
reg, err := license.UploadLicenseFile(filename)
|
||||
reg, err := dpp.Driver.UploadLicenseFile(filename)
|
||||
if err != nil {
|
||||
modal.Alert("That license key didn't seem quite right.").WithTitle("License Error")
|
||||
return
|
||||
|
|
|
@ -171,9 +171,8 @@ func NewOpenLevelEditor(config OpenLevelEditor) *ui.Window {
|
|||
* Frame for selecting User Doodads
|
||||
******************/
|
||||
|
||||
// Doodads not shown if we're loading a map to play, nor are they
|
||||
// available to the free version.
|
||||
if !config.LoadForPlay && !balance.FreeVersion {
|
||||
// Doodads not shown if we're loading a map to play.
|
||||
if !config.LoadForPlay {
|
||||
label2 := ui.NewLabel(ui.Label{
|
||||
Text: "Doodads",
|
||||
Font: balance.LabelFont,
|
||||
|
|
Loading…
Reference in New Issue
Block a user