Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

store/lines: Made the TPS check if there is a time in it #194

Merged
merged 1 commit into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions action/action.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package action

import (
"time"

"github.com/xescugc/maze-wars/utils"
"github.com/xescugc/maze-wars/utils/graph"
"nhooyr.io/websocket"
Expand All @@ -27,6 +29,7 @@ type Action struct {
StartGame *StartGamePayload `json:"start_game,omitempty"`
GoHome *GoHomePayload `json:"go_home,omitempty"`
ToggleStats *ToggleStatsPayload `json:"toggle_stats,omitempty"`
TPS *TPSPayload `json:"tps,omitempty"`

OpenTowerMenu *OpenTowerMenuPayload `json:"open_tower_menu,omitempty"`
CloseTowerMenu *CloseTowerMenuPayload `json:"close_tower_menu,omitempty"`
Expand Down Expand Up @@ -79,9 +82,16 @@ func NewSummonUnit(t, pid string, plid, clid int) *Action {
}
}

func NewTPS() *Action {
type TPSPayload struct {
Time time.Time
}

func NewTPS(t time.Time) *Action {
return &Action{
Type: TPS,
TPS: &TPSPayload{
Time: t,
},
}
}

Expand Down Expand Up @@ -509,8 +519,9 @@ type SyncStateUnitPayload struct {

Health float64

Path []graph.Step
HashPath string
Path []graph.Step
HashPath string
CreatedAt time.Time
}

// TODO: or make the action.Action separated or make the store.Player separated
Expand Down
2 changes: 1 addition & 1 deletion client/game/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (ac *ActionDispatcher) SummonUnit(unit, pid string, plid, clid int) {

// TPS is the call for every TPS event
func (ac *ActionDispatcher) TPS() {
tpsa := action.NewTPS()
tpsa := action.NewTPS(time.Time{})
ac.Dispatch(tpsa)
}

Expand Down
6 changes: 1 addition & 5 deletions server/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,6 @@ func (ac *ActionDispatcher) WaitRoomCountdownTick() {
ac.startGame()
}

func (ac *ActionDispatcher) TPS(rooms *RoomsStore) {
tpsa := action.NewTPS()
ac.Dispatch(tpsa)
}

func (ac *ActionDispatcher) UserSignUp(un string) {
ac.Dispatch(action.NewUserSignUp(un))
}
Expand All @@ -103,6 +98,7 @@ func (ac *ActionDispatcher) UserSignOut(un string) {
}

func (ac *ActionDispatcher) SyncState(rooms *RoomsStore) {
ac.Dispatch(action.NewTPS(time.Now()))
rstate := rooms.GetState().(RoomsState)
for _, r := range rstate.Rooms {
if r.Name == rstate.CurrentWaitingRoom {
Expand Down
Binary file modified server/assets/wasm/maze-wars.wasm
Binary file not shown.
4 changes: 0 additions & 4 deletions server/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ func startLoop(ctx context.Context, s *Store) {
stateTicker := time.NewTicker(time.Second / 4)
// The default TPS on of Ebiten client if 60 so to
// emulate that we trigger the move action every TPS
tpsTicker := time.NewTicker(time.Second / 60)
usersTicker := time.NewTicker(5 * time.Second)
for {
select {
Expand All @@ -190,14 +189,11 @@ func startLoop(ctx context.Context, s *Store) {
actionDispatcher.IncomeTick(s.Rooms)
actionDispatcher.WaitRoomCountdownTick()
actionDispatcher.SyncWaitingRoom(s.Rooms)
case <-tpsTicker.C:
actionDispatcher.TPS(s.Rooms)
case <-usersTicker.C:
actionDispatcher.SyncUsers(s.Users)
case <-ctx.Done():
stateTicker.Stop()
secondTicker.Stop()
tpsTicker.Stop()
usersTicker.Stop()
goto FINISH
}
Expand Down
91 changes: 72 additions & 19 deletions store/lines.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package store

import (
"sync"
"time"

"github.com/gofrs/uuid"
"github.com/xescugc/go-flux"
Expand All @@ -12,7 +13,13 @@ import (
"github.com/xescugc/maze-wars/utils/graph"
)

const atScale = true
const (
atScale = true
)

var (
tpsMS = (time.Second / 60).Milliseconds()
)

type Lines struct {
*flux.ReduceStore
Expand All @@ -31,6 +38,17 @@ type Line struct {
Units map[string]*Unit

Graph *graph.Graph

// UpdatedAt is the last time
// something was updated on this Line.
// Towers added, Units added or
// when the Units position was updated
// the last time.
// Used for the SyncState to know how much
// time has passed since the last update
// and move the Units accordingly
// (60 moves per second pass)
UpdatedAt time.Time
}

type Tower struct {
Expand Down Expand Up @@ -59,6 +77,12 @@ type Unit struct {

Path []graph.Step
HashPath string

// CreatedAt has the time of creation so
// on the next SyncState will be moved just
// the diff amount and then it'll be set to 'nil'
// so we know it's on sync
CreatedAt time.Time
}

func (u *Unit) FacesetKey() string { return unit.Units[u.Type].FacesetKey() }
Expand Down Expand Up @@ -168,19 +192,13 @@ func (ls *Lines) Reduce(state, a interface{}) interface{} {

l.Towers[tw.ID] = tw

for _, u := range l.Units {
u.Path = l.Graph.AStar(u.X, u.Y, u.Facing, l.Graph.DeathNode.X, l.Graph.DeathNode.Y, atScale)
u.HashPath = graph.HashSteps(u.Path)
}
recalculateLineUnitSteps(l)
case action.RemoveTower:
// TODO: Add the LineID
for _, l := range lstate.Lines {
if ok := l.Graph.RemoveTower(act.RemoveTower.TowerID); ok {
delete(l.Towers, act.RemoveTower.TowerID)
for _, u := range l.Units {
u.Path = l.Graph.AStar(u.X, u.Y, u.Facing, l.Graph.DeathNode.X, l.Graph.DeathNode.Y, atScale)
u.HashPath = graph.HashSteps(u.Path)
}
recalculateLineUnitSteps(l)
}
}
case action.TowerAttack:
Expand Down Expand Up @@ -225,6 +243,7 @@ func (ls *Lines) Reduce(state, a interface{}) interface{} {
PlayerLineID: act.SummonUnit.PlayerLineID,
CurrentLineID: act.SummonUnit.CurrentLineID,
Health: unit.Units[act.SummonUnit.Type].Health,
CreatedAt: time.Now(),
}

u.Path = l.Graph.AStar(u.X, u.Y, u.Facing, l.Graph.DeathNode.X, l.Graph.DeathNode.Y, atScale)
Expand Down Expand Up @@ -252,6 +271,7 @@ func (ls *Lines) Reduce(state, a interface{}) interface{} {
u.Path = nl.Graph.AStar(u.X, u.Y, u.Facing, nl.Graph.DeathNode.X, nl.Graph.DeathNode.Y, atScale)
u.HashPath = graph.HashSteps(u.Path)

u.CreatedAt = time.Now()
nl.Units[u.ID] = u

break
Expand All @@ -273,16 +293,7 @@ func (ls *Lines) Reduce(state, a interface{}) interface{} {
defer ls.mxLines.Unlock()

for _, l := range lstate.Lines {
for _, u := range l.Units {
if len(u.Path) > 0 {
nextStep := u.Path[0]
u.Path = u.Path[1:]
u.MovingCount += 1
u.Y = nextStep.Y
u.X = nextStep.X
u.Facing = nextStep.Facing
}
}
moveLineUnitsTo(l, act.TPS.Time)
}
case action.RemovePlayer:
ls.mxLines.Lock()
Expand Down Expand Up @@ -365,6 +376,48 @@ func (ls *Lines) Reduce(state, a interface{}) interface{} {
return lstate
}

func recalculateLineUnitSteps(l *Line) {
t := time.Now()
moveLineUnitsTo(l, t)

for _, u := range l.Units {
u.Path = l.Graph.AStar(u.X, u.Y, u.Facing, l.Graph.DeathNode.X, l.Graph.DeathNode.Y, atScale)
u.HashPath = graph.HashSteps(u.Path)
}
}

func moveLineUnitsTo(l *Line, t time.Time) {
lmoves := 1
if !t.IsZero() && !l.UpdatedAt.IsZero() {
lmoves = int(t.Sub(l.UpdatedAt).Milliseconds() / tpsMS)
}
for _, u := range l.Units {
if len(u.Path) > 0 {
umoves := lmoves
if !t.IsZero() && !u.CreatedAt.IsZero() {
umoves = int(t.Sub(u.CreatedAt).Milliseconds() / tpsMS)
// This way we mean it's up to date now
u.CreatedAt = time.Time{}
}
// If we have less moves remaining that the expected amount
// we just move to the last position
if len(u.Path) < umoves {
umoves = len(u.Path) - 1
}
if umoves == 0 {
continue
}
nextStep := u.Path[umoves-1]
u.Path = u.Path[umoves:]
u.MovingCount += umoves
u.Y = nextStep.Y
u.X = nextStep.X
u.Facing = nextStep.Facing
}
}
l.UpdatedAt = t
}

func (ls *Lines) newLine(lid int) *Line {
x, y := ls.store.Map.GetHomeCoordinates(lid)
g, err := graph.New(x+16, y+16, 16, 84, 16, 7, 74, 3)
Expand Down
81 changes: 63 additions & 18 deletions store/reduce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"sort"
"sync"
"testing"
"time"

"github.com/gofrs/uuid"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -237,24 +238,57 @@ func TestSummonUnit(t *testing.T) {
func TestTPS(t *testing.T) {
addAction(action.TPS.String())
t.Run("Success", func(t *testing.T) {
s := initStore()
p := addPlayer(s)
p2 := addPlayer(s)
s.Dispatch(action.NewStartGame())
ms, ls := startGame(t, s)
p, u := summonUnit(s, p, p2)

s.Dispatch(action.NewTPS())

ps := playersInitialState()
ps.Players[p.ID] = &p
ps.Players[p2.ID] = &p2

u.Path = u.Path[1:]
u.MovingCount++
ls.Lines[p2.LineID].Units[u.ID] = &u

equalStore(t, s, ps, ms, ls)
t.Run("Default", func(t *testing.T) {
s := initStore()
p := addPlayer(s)
p2 := addPlayer(s)
s.Dispatch(action.NewStartGame())
ms, ls := startGame(t, s)
p, u := summonUnit(s, p, p2)

s.Dispatch(action.NewTPS(time.Time{}))

ps := playersInitialState()
ps.Players[p.ID] = &p
ps.Players[p2.ID] = &p2

u.Path = u.Path[1:]
u.MovingCount++
ls.Lines[p2.LineID].Units[u.ID] = &u

equalStore(t, s, ps, ms, ls)
})
t.Run("WithTime", func(t *testing.T) {
s := initStore()
p := addPlayer(s)
p2 := addPlayer(s)
s.Dispatch(action.NewStartGame())
ms, ls := startGame(t, s)
p, u := summonUnit(s, p, p2)

l2 := s.Lines.GetState().(store.LinesState).Lines[p2.LineID]

tn := time.Now()
l2.UpdatedAt = tn
l2.Units[u.ID].CreatedAt = tn

ta := tn.Add(time.Second)
s.Dispatch(action.NewTPS(ta))

ps := playersInitialState()
ps.Players[p.ID] = &p
ps.Players[p2.ID] = &p2

np := u.Path[61]
u.Path = u.Path[62:]
u.MovingCount += 62
u.X = np.X
u.Y = np.Y
u.Facing = np.Facing
ls.Lines[p2.LineID].Units[u.ID] = &u

equalStore(t, s, ps, ms, ls)
})
})
}

Expand Down Expand Up @@ -489,6 +523,8 @@ func TestTowerAttack(t *testing.T) {
p := addPlayer(s)
p2 := addPlayer(s)
s.Dispatch(action.NewStartGame())
// TODO: Each summon/place updates the l.UpdatedAt so we should
// manually add the value from the store line
ms, ls := startGame(t, s)
p, tw := placeTower(s, p)
p, u := summonUnit(s, p, p2)
Expand Down Expand Up @@ -634,6 +670,7 @@ func TestChangeUnitLine(t *testing.T) {

// As this are random assigned we cannot expect them
u1.ID, u1.X, u1.Y = units[uid].ID, units[uid].X, units[uid].Y
u1.CreatedAt = units[uid].CreatedAt

// We need to set the path after the X, Y are set
u1.Path = l.Graph.AStar(u1.X, u1.Y, u1.Facing, l.Graph.DeathNode.X, l.Graph.DeathNode.Y, atScale)
Expand Down Expand Up @@ -763,9 +800,17 @@ func equalStore(t *testing.T, sto *store.Store, states ...interface{}) {
// to have the Units/Towers init
for _, l := range lis.Lines {
l.Graph = nil
l.UpdatedAt = time.Time{}
for _, u := range l.Units {
u.CreatedAt = time.Time{}
}
}
for _, l := range sto.Lines.GetState().(store.LinesState).Lines {
l.Graph = nil
l.UpdatedAt = time.Time{}
for _, u := range l.Units {
u.CreatedAt = time.Time{}
}
}
assert.Equal(t, lis, sto.Lines.GetState().(store.LinesState))
assert.Equal(t, mis, sto.Map.GetState().(store.MapState))
Expand Down
Loading