~nromdotcom/gemif

cfe53b48c2913a5ccb4871542a2f0b5be6bb468a — Norm MacLennan 1 year, 2 days ago e0ece7e
Make serlialization format an enum for earlier error detection
3 files changed, 51 insertions(+), 15 deletions(-)

M cmd/gemif/main.go
M pkg/gamemanager/engineconfig.go
M pkg/gamemanager/gamemanager.go
M cmd/gemif/main.go => cmd/gemif/main.go +5 -1
@@ 37,7 37,11 @@ func main() {

	renderDesc := config.Get("engine.render_descriptions").(bool)
	tokenFormat := config.Get("engine.statetoken_format").(string)
	engineConfig := gamemanager.NewEngineConfig(renderDesc, tokenFormat)

	engineConfig, err := gamemanager.NewEngineConfig(renderDesc, tokenFormat)
	if err != nil {
		log.Fatalf("Error loading config: %s", err)
	}

	gameMgr, err := gamemanager.NewGameManager(book, engineConfig)
	if err != nil {

M pkg/gamemanager/engineconfig.go => pkg/gamemanager/engineconfig.go +34 -4
@@ 1,15 1,45 @@
package gamemanager

import (
	"errors"
	"fmt"
)

// EngineConfig contains settings information for driving the engine.
type EngineConfig struct {
	RenderDescriptions bool
	StateTokenFormat   string
	StateTokenFormat   SerializationFormat
}

// SerializationFormat determines which format StateTokens are serialized as.
type SerializationFormat int

const (
	json SerializationFormat = iota + 1
	protobuf
)

func getSerDeFormat(format string) SerializationFormat {
	serializationFormats := map[string]SerializationFormat{
		"json":  json,
		"proto": protobuf,
	}

	return serializationFormats[format]
}

// ErrInvalidSerializationFormat occurs when engine config specifies a unsupported format.
var ErrInvalidSerializationFormat = errors.New("invalid serialization format")

// NewEngineConfig news up an EngineConfig based on passed in values.
func NewEngineConfig(renderDesc bool, tokenFormat string) EngineConfig {
func NewEngineConfig(renderDesc bool, tokenFormat string) (EngineConfig, error) {
	serdeFormat := getSerDeFormat(tokenFormat)
	if serdeFormat == 0 {
		return EngineConfig{}, fmt.Errorf("%w: %s", ErrInvalidSerializationFormat, tokenFormat)
	}

	return EngineConfig{
		RenderDescriptions: renderDesc,
		StateTokenFormat:   tokenFormat,
	}
		StateTokenFormat:   serdeFormat,
	}, nil
}

M pkg/gamemanager/gamemanager.go => pkg/gamemanager/gamemanager.go +12 -10
@@ 22,8 22,6 @@ type SpeculativeState struct {
}

var (
	// ErrInvalidSerializationFormat occurs when engine config specifies a unsupported format.
	ErrInvalidSerializationFormat = errors.New("invalid serialization format")
	// ErrRoomNotFound occurs when the room doesn't exist in the given story.
	ErrRoomNotFound = errors.New("room not found")
	// ErrExitNotFound occurs when the exit doesn't exit in the given room.


@@ 81,14 79,17 @@ func (gm *GameManager) DeserializeState(stateToken string) (GameState, error) {

	var st StateToken

	if gm.EngineConfig.StateTokenFormat == "proto" {
		if unmarshallErr := proto.Unmarshal(decodedString, &st); unmarshallErr != nil {
	switch gm.EngineConfig.StateTokenFormat {
	case json:
		if unmarshallErr := protojson.Unmarshal(decodedString, &st); unmarshallErr != nil {
			return GameState{}, fmt.Errorf("failed to load state: %w", unmarshallErr)
		}
	} else if gm.EngineConfig.StateTokenFormat == "json" {
		if unmarshallErr := protojson.Unmarshal(decodedString, &st); unmarshallErr != nil {
	case protobuf:
		if unmarshallErr := proto.Unmarshal(decodedString, &st); unmarshallErr != nil {
			return GameState{}, fmt.Errorf("failed to load state: %w", unmarshallErr)
		}
	default:
		return GameState{}, fmt.Errorf("%w: %d", ErrInvalidSerializationFormat, gm.EngineConfig.StateTokenFormat)
	}

	return GameState{


@@ 106,23 107,24 @@ func (gm *GameManager) SerializeState(gameState GameState) (string, error) {
		Conditions:  gameState.Conditions,
	}

	if gm.EngineConfig.StateTokenFormat == "proto" {
	switch gm.EngineConfig.StateTokenFormat {
	case protobuf:
		stateBytes, err := proto.Marshal(&st)
		if err != nil {
			return "", fmt.Errorf("failed to save state: %w", err)
		}

		return base64.StdEncoding.EncodeToString(stateBytes), nil
	} else if gm.EngineConfig.StateTokenFormat == "json" {
	case json:
		stateBytes, err := protojson.Marshal(&st)
		if err != nil {
			return "", fmt.Errorf("failed to save state: %w", err)
		}

		return base64.StdEncoding.EncodeToString(stateBytes), nil
	default:
		return "", fmt.Errorf("%w: %d", ErrInvalidSerializationFormat, gm.EngineConfig.StateTokenFormat)
	}

	return "", fmt.Errorf("%w: %s", ErrInvalidSerializationFormat, gm.EngineConfig.StateTokenFormat)
}

// ConstructSpeculativeStates makes a SpeculativeState from the active GameState and actions available in currentRoom.