diff --git a/README.md b/README.md index dd6bce7..a7101fa 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ commands to the handler. ## Usage -1. Compile your `.yarn` file. You can probably get the compiled output from a +1. Compile your `.yarn` file. You can probably get the compiled output from a Unity project, or you can compile without using Unity with a tool like the [Yarn Spinner Console](https://github.com/YarnSpinnerTool/YarnSpinner-Console): @@ -75,7 +75,7 @@ commands to the handler. return choice, nil } - // ... and also the other methods. + // ... and also the other methods. // Alternatively you can embed yarn.FakeDialogueHandler in your handler. ``` @@ -85,9 +85,9 @@ commands to the handler. ```go package main - + import "github.com/DrJosh9000/yarn" - + func main() { // Load the files (error handling omitted for brevity): program, stringTable, _ := yarn.LoadFiles("Example.yarn.yarnc", "Example.yarn.csv", "en-AU") @@ -99,7 +99,7 @@ commands to the handler. vm := &yarn.VirtualMachine{ Program: program, Handler: myHandler, - Vars: make(yarn.MapVariableStorage), // or your own VariableStorage implementation + Vars: yarn.NewMapVariableStorage(), // or your own VariableStorage implementation FuncMap: yarn.FuncMap{ // this is optional "last_value": func(x ...interface{}) interface{} { return x[len(x)-1] @@ -168,7 +168,7 @@ func (m *MyHandler) setWaiting(w bool) { func (m *MyHandler) Line(line yarn.Line) error { text, _ := m.stringTable.Render(line) m.dialogueDisplay.Show(text) - + // Go into waiting-for-player-input state m.setWaiting(true) diff --git a/cmd/yarnrunner.go b/cmd/yarnrunner.go index 522c312..4635e8f 100644 --- a/cmd/yarnrunner.go +++ b/cmd/yarnrunner.go @@ -44,7 +44,7 @@ func main() { langCode := flag.String("lang", "en-AU", "Language tag (BCP 47)") flag.Parse() - program, stringTable, err := yarn.LoadFiles(*yarncFilename, *csvFilename, *langCode) + program, stringTable, err := yarn.LoadFiles(*yarncFilename, *langCode) if err != nil { log.Fatalf("Loading files: %v", err) } @@ -54,7 +54,7 @@ func main() { Handler: &dialogueHandler{ stringTable: stringTable, }, - Vars: make(yarn.MapVariableStorage), + Vars: yarn.NewMapVariableStorage(), } if err := vm.Run(*startNode); err != nil { log.Printf("Yarn VM error: %v", err) diff --git a/vars.go b/vars.go index 4bec47c..b579f59 100644 --- a/vars.go +++ b/vars.go @@ -14,30 +14,48 @@ package yarn +import "sync" + // VariableStorage stores values of any kind. type VariableStorage interface { Clear() - GetValue(name string) (value interface{}, ok bool) - SetValue(name string, value interface{}) + GetValue(name string) (value any, ok bool) + SetValue(name string, value any) } // MapVariableStorage implements VariableStorage, in memory, using a map. -type MapVariableStorage map[string]interface{} +type MapVariableStorage struct { + mu sync.RWMutex + m map[string]any +} + +// NewMapVariableStorage creates a new empty MapVariableStorage. +func NewMapVariableStorage() *MapVariableStorage { + return &MapVariableStorage{ + m: make(map[string]any), + } +} // Clear empties the storage of all values. -func (m MapVariableStorage) Clear() { - for name := range m { - delete(m, name) +func (m *MapVariableStorage) Clear() { + m.mu.Lock() + defer m.mu.Unlock() + for name := range m.m { + delete(m.m, name) } } // GetValue fetches a value from the map, returning (nil, false) if not present. -func (m MapVariableStorage) GetValue(name string) (value interface{}, found bool) { - value, found = m[name] +func (m *MapVariableStorage) GetValue(name string) (value any, found bool) { + m.mu.RLock() + defer m.mu.RUnlock() + value, found = m.m[name] return value, found } // SetValue sets a value in the map. -func (m MapVariableStorage) SetValue(name string, value interface{}) { - m[name] = value +func (m *MapVariableStorage) SetValue(name string, value any) { + m.mu.Lock() + defer m.mu.Unlock() + m.m[name] = value } diff --git a/vm_test.go b/vm_test.go index 19bb632..54648d2 100644 --- a/vm_test.go +++ b/vm_test.go @@ -47,7 +47,7 @@ func TestAllTestPlans(t *testing.T) { vm := &VirtualMachine{ Program: prog, Handler: testplan, - Vars: make(MapVariableStorage), + Vars: NewMapVariableStorage(), FuncMap: FuncMap{ // Used by various "assert": func(x interface{}) error {