Add the concept of commands to the world and executing them
This commit is contained in:
parent
013a69fa63
commit
e5d5d123a6
6 changed files with 163 additions and 19 deletions
23
pkg/game/command.go
Normal file
23
pkg/game/command.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package game
|
||||||
|
|
||||||
|
import "github.com/google/uuid"
|
||||||
|
|
||||||
|
// A command is simply a function that acts on the a given instance in the world
|
||||||
|
type Command func() error
|
||||||
|
|
||||||
|
// CommandMove will move the instance in question
|
||||||
|
func (w *World) CommandMove(id uuid.UUID, vec Vector) Command {
|
||||||
|
return func() error {
|
||||||
|
// Move the instance
|
||||||
|
_, err := w.MovePosition(id, vec)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommandSpawn
|
||||||
|
// TODO: Two spawn commands with the same id could trigger a fail later on, we should prevent that somehow
|
||||||
|
func (w *World) CommandSpawn(id uuid.UUID) Command {
|
||||||
|
return func() error {
|
||||||
|
return w.Spawn(id)
|
||||||
|
}
|
||||||
|
}
|
50
pkg/game/command_test.go
Normal file
50
pkg/game/command_test.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCommand_Spawn(t *testing.T) {
|
||||||
|
world := NewWorld()
|
||||||
|
a := uuid.New()
|
||||||
|
|
||||||
|
spawnCommand := world.CommandSpawn(a)
|
||||||
|
assert.NoError(t, world.Execute(spawnCommand), "Failed to execute spawn command")
|
||||||
|
|
||||||
|
instance, ok := world.Instances[a]
|
||||||
|
assert.True(t, ok, "No new instance in world")
|
||||||
|
assert.Equal(t, a, instance.id, "New instance has incorrect id")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommand_Move(t *testing.T) {
|
||||||
|
world := NewWorld()
|
||||||
|
a := uuid.New()
|
||||||
|
assert.NoError(t, world.Spawn(a), "Failed to spawn")
|
||||||
|
|
||||||
|
pos := Vector{
|
||||||
|
X: 1.0,
|
||||||
|
Y: 2.0,
|
||||||
|
Z: 3.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := world.SetPosition(a, pos)
|
||||||
|
assert.NoError(t, err, "Failed to set position for instance")
|
||||||
|
|
||||||
|
move := Vector{
|
||||||
|
X: 3.0,
|
||||||
|
Y: 2.0,
|
||||||
|
Z: 1.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try the move command
|
||||||
|
moveCommand := world.CommandMove(a, move)
|
||||||
|
assert.NoError(t, world.Execute(moveCommand), "Failed to execute move command")
|
||||||
|
|
||||||
|
newpos, err := world.GetPosition(a)
|
||||||
|
assert.NoError(t, err, "Failed to set position for instance")
|
||||||
|
pos.Add(move)
|
||||||
|
assert.Equal(t, pos, newpos, "Failed to correctly set position for instance")
|
||||||
|
}
|
|
@ -1,6 +1,15 @@
|
||||||
package game
|
package game
|
||||||
|
|
||||||
type Position struct {
|
// Vector desribes a 3D vector
|
||||||
X int `json:"x"`
|
type Vector struct {
|
||||||
Y int `json:"y"`
|
X float64 `json:"x"`
|
||||||
|
Y float64 `json:"y"`
|
||||||
|
Z float64 `json:"z"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds one vector to another
|
||||||
|
func (v *Vector) Add(v2 Vector) {
|
||||||
|
v.X += v2.X
|
||||||
|
v.Y += v2.Y
|
||||||
|
v.Z += v2.Z
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,8 @@ import (
|
||||||
|
|
||||||
// World describes a self contained universe and everything in it
|
// World describes a self contained universe and everything in it
|
||||||
type World struct {
|
type World struct {
|
||||||
|
// Instances is a map of all the instances in the game
|
||||||
Instances map[uuid.UUID]Instance `json:"instances"`
|
Instances map[uuid.UUID]Instance `json:"instances"`
|
||||||
|
|
||||||
// dataPath is the location for the data to be stored
|
|
||||||
dataPath string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance describes a single entity or instance of an entity in the world
|
// Instance describes a single entity or instance of an entity in the world
|
||||||
|
@ -20,7 +18,7 @@ type Instance struct {
|
||||||
id uuid.UUID
|
id uuid.UUID
|
||||||
|
|
||||||
// pos represents where this instance is in the world
|
// pos represents where this instance is in the world
|
||||||
pos Position
|
pos Vector
|
||||||
}
|
}
|
||||||
|
|
||||||
const kWorldFileName = "rove-world.json"
|
const kWorldFileName = "rove-world.json"
|
||||||
|
@ -32,9 +30,11 @@ func NewWorld() *World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds an instance to the game
|
// Spawn adds an instance to the game
|
||||||
func (w *World) CreateInstance() uuid.UUID {
|
func (w *World) Spawn(id uuid.UUID) error {
|
||||||
id := uuid.New()
|
if _, ok := w.Instances[id]; ok {
|
||||||
|
return fmt.Errorf("instance with id %s already exists in world", id)
|
||||||
|
}
|
||||||
|
|
||||||
// Initialise the instance
|
// Initialise the instance
|
||||||
instance := Instance{
|
instance := Instance{
|
||||||
|
@ -44,7 +44,7 @@ func (w *World) CreateInstance() uuid.UUID {
|
||||||
// Append the instance to the list
|
// Append the instance to the list
|
||||||
w.Instances[id] = instance
|
w.Instances[id] = instance
|
||||||
|
|
||||||
return id
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removes an instance from the game
|
// Removes an instance from the game
|
||||||
|
@ -58,10 +58,42 @@ func (w *World) DestroyInstance(id uuid.UUID) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPosition returns the position of a given instance
|
// GetPosition returns the position of a given instance
|
||||||
func (w World) GetPosition(id uuid.UUID) (Position, error) {
|
func (w World) GetPosition(id uuid.UUID) (Vector, error) {
|
||||||
if i, ok := w.Instances[id]; ok {
|
if i, ok := w.Instances[id]; ok {
|
||||||
return i.pos, nil
|
return i.pos, nil
|
||||||
} else {
|
} else {
|
||||||
return Position{}, fmt.Errorf("no instance matching id")
|
return Vector{}, fmt.Errorf("no instance matching id")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetPosition sets an instances position
|
||||||
|
func (w *World) SetPosition(id uuid.UUID, pos Vector) error {
|
||||||
|
if i, ok := w.Instances[id]; ok {
|
||||||
|
i.pos = pos
|
||||||
|
w.Instances[id] = i
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("no instance matching id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPosition sets an instances position
|
||||||
|
func (w *World) MovePosition(id uuid.UUID, vec Vector) (Vector, error) {
|
||||||
|
if i, ok := w.Instances[id]; ok {
|
||||||
|
i.pos.Add(vec)
|
||||||
|
w.Instances[id] = i
|
||||||
|
return i.pos, nil
|
||||||
|
} else {
|
||||||
|
return Vector{}, fmt.Errorf("no instance matching id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute will run the commands given
|
||||||
|
func (w *World) Execute(commands ...Command) error {
|
||||||
|
for _, c := range commands {
|
||||||
|
if err := c(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package game
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,8 +17,10 @@ func TestNewWorld(t *testing.T) {
|
||||||
|
|
||||||
func TestWorld_CreateInstance(t *testing.T) {
|
func TestWorld_CreateInstance(t *testing.T) {
|
||||||
world := NewWorld()
|
world := NewWorld()
|
||||||
a := world.CreateInstance()
|
a := uuid.New()
|
||||||
b := world.CreateInstance()
|
b := uuid.New()
|
||||||
|
assert.NoError(t, world.Spawn(a), "Failed to spawn")
|
||||||
|
assert.NoError(t, world.Spawn(b), "Failed to spawn")
|
||||||
|
|
||||||
// Basic duplicate check
|
// Basic duplicate check
|
||||||
if a == b {
|
if a == b {
|
||||||
|
@ -29,8 +32,10 @@ func TestWorld_CreateInstance(t *testing.T) {
|
||||||
|
|
||||||
func TestWorld_DestroyInstance(t *testing.T) {
|
func TestWorld_DestroyInstance(t *testing.T) {
|
||||||
world := NewWorld()
|
world := NewWorld()
|
||||||
a := world.CreateInstance()
|
a := uuid.New()
|
||||||
b := world.CreateInstance()
|
b := uuid.New()
|
||||||
|
assert.NoError(t, world.Spawn(a), "Failed to spawn")
|
||||||
|
assert.NoError(t, world.Spawn(b), "Failed to spawn")
|
||||||
|
|
||||||
err := world.DestroyInstance(a)
|
err := world.DestroyInstance(a)
|
||||||
assert.NoError(t, err, "Error returned from instance destroy")
|
assert.NoError(t, err, "Error returned from instance destroy")
|
||||||
|
@ -42,3 +47,27 @@ func TestWorld_DestroyInstance(t *testing.T) {
|
||||||
t.Error("Remaining instance is incorrect")
|
t.Error("Remaining instance is incorrect")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWorld_GetSetMovePosition(t *testing.T) {
|
||||||
|
world := NewWorld()
|
||||||
|
a := uuid.New()
|
||||||
|
assert.NoError(t, world.Spawn(a), "Failed to spawn")
|
||||||
|
|
||||||
|
pos := Vector{
|
||||||
|
X: 1.0,
|
||||||
|
Y: 2.0,
|
||||||
|
Z: 3.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := world.SetPosition(a, pos)
|
||||||
|
assert.NoError(t, err, "Failed to set position for instance")
|
||||||
|
|
||||||
|
newpos, err := world.GetPosition(a)
|
||||||
|
assert.NoError(t, err, "Failed to set position for instance")
|
||||||
|
assert.Equal(t, pos, newpos, "Failed to correctly set position for instance")
|
||||||
|
|
||||||
|
newpos, err = world.MovePosition(a, pos)
|
||||||
|
assert.NoError(t, err, "Failed to set position for instance")
|
||||||
|
pos.Add(pos)
|
||||||
|
assert.Equal(t, pos, newpos, "Failed to correctly move position for instance")
|
||||||
|
}
|
||||||
|
|
|
@ -155,7 +155,7 @@ type SpawnData struct {
|
||||||
type SpawnResponse struct {
|
type SpawnResponse struct {
|
||||||
BasicResponse
|
BasicResponse
|
||||||
|
|
||||||
Position game.Position `json:"position"`
|
Position game.Vector `json:"position"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleSpawn will spawn the player entity for the associated account
|
// HandleSpawn will spawn the player entity for the associated account
|
||||||
|
@ -192,7 +192,8 @@ func (s *Server) HandleSpawn(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Printf("\tspawn data: %v\n", data)
|
fmt.Printf("\tspawn data: %v\n", data)
|
||||||
|
|
||||||
// Create a new instance
|
// Create a new instance
|
||||||
inst := s.world.CreateInstance()
|
inst := uuid.New()
|
||||||
|
s.world.Spawn(inst)
|
||||||
if pos, err := s.world.GetPosition(inst); err != nil {
|
if pos, err := s.world.GetPosition(inst); err != nil {
|
||||||
response.Error = fmt.Sprint("No position found for created instance")
|
response.Error = fmt.Sprint("No position found for created instance")
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue