-
Notifications
You must be signed in to change notification settings - Fork 0
/
runtime.go
180 lines (148 loc) · 4.36 KB
/
runtime.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package runtime
import (
"log"
"time"
"stabbey/interfaces"
)
var TURN_DELAY, _ = time.ParseDuration("500ms")
var GAME interfaces.Game
var COMMAND_CODES = map[int]string{
0: "start game",
1: "update tick",
2: "set queue"}
type Runtime struct {
/* Reads from this to handle any orders */
orderStream chan interfaces.Order
}
func New(game interfaces.Game) *Runtime {
r := &Runtime{}
log.Println("CREATING NEW GAME RUNTIME")
r.orderStream = make(chan interfaces.Order)
GAME = game
go r.processOrders()
go r.scheduleActions()
return r
}
/* Goroutine to process incoming orders from players */
func (r *Runtime) processOrders() {
for {
order := <-r.orderStream
if COMMAND_CODES[order.GetCommandCode()] == "update tick" {
// Don't need to specialy handle this, since all commands may
// update the tick number as part of them
} else if COMMAND_CODES[order.GetCommandCode()] == "set queue" {
entity := GAME.GetEntityByPlayer(order.GetPlayer())
if len(order.GetActions()) <= 0 {
log.Printf("Ignoring %v's set queue: no actions set",
order.GetPlayer().GetName())
continue
}
entity.SetActionQueue(order.GetActions())
}
// Must be done after the rest of the order processing to make sure
// that the game state update routine doesn't race condition us
updateTick(order)
}
}
/* Dumps the given order to the console */
func printOrder(order interfaces.Order) {
entity := GAME.GetEntityByPlayer(order.GetPlayer())
log.Printf("Handled Order: command:'%v' tick:%v actions:%v player:%v",
COMMAND_CODES[order.GetCommandCode()], order.GetTickNumber(),
entity.GetStringActionQueue(),
order.GetPlayer().GetPlayerId())
}
/* Allows for player orders to be added */
func (r *Runtime) AddOrder(order interfaces.Order) {
r.orderStream <- order
}
/* Goroutine to run queued actions & send updates to players */
func (r *Runtime) scheduleActions() {
/* The first time all players are ready, we just send the initial state */
for {
if allPlayersReady() {
log.Println("Players ready; sent initial game state")
GAME.Run()
GAME.SetLastTick(GAME.GetLastTick() + 1)
broadcastGamestate()
break
}
time.Sleep(TURN_DELAY)
}
/* Thereafter, the players are only ready if they've a queue */
for {
/* Ensure all players are ready */
if allPlayersReady() == false {
time.Sleep(TURN_DELAY)
continue
}
/* Do all the player actions */
for i := 0; i < len(GAME.GetPlayers()); i++ {
player := GAME.GetPlayer(i)
entity := GAME.GetEntityByPlayer(player)
if action := entity.PopAction(); action != nil {
act(entity, action)
} else {
log.Printf("Player %v didn't have move ready "+
"(client set queue error)!"+
" This'll count as 1 do nothing.", i)
}
GAME.SetLastTick(GAME.GetLastTick() + 1)
broadcastGamestate()
time.Sleep(TURN_DELAY)
}
/* Do all the other turns (mostly monster moves) */
for _, entity := range GAME.GetEntities() {
// Don't trigger entities that are on different boards
if bId, _, _ := entity.GetPosition(); bId != GAME.GetCurrentBoard() {
continue
}
// Don't trigger dead entities
if entity.IsDead() {
continue
}
if entity.RunTurn(GAME.GetLastTick()) {
GAME.SetLastTick(GAME.GetLastTick() + 1)
broadcastGamestate()
log.Println(entity.GetName(), "did stuff. Broadcasting...")
time.Sleep(TURN_DELAY)
}
}
}
}
/* Checks whether all players are ready (done queueing) */
func allPlayersReady() bool {
players := GAME.GetPlayers()
/* Has to be at least one ready player */
if len(players) == 0 {
return false
}
for _, player := range players {
/* Ready players are 1 ahead of the game's tick */
if player.GetLastTick() <= GAME.GetLastTick() {
return false
}
}
return true
}
/* Sends the gamestate to everyone */
func broadcastGamestate() {
for _, player := range GAME.GetPlayers() {
player.SendMessage(GAME.Json(player))
}
for _, spectator := range GAME.GetSpectators() {
spectator.SendMessage(GAME.Json(nil))
}
}
/* Generic action handler for any entity */
func act(entity interfaces.Entity, action interfaces.Action) {
if err := action.Act(entity, GAME); err != nil {
log.Printf("%v: %v", entity.GetName(), err)
}
}
/* Updates players' ticks and send out gamestate when everyone's ready */
func updateTick(order interfaces.Order) {
p := order.GetPlayer()
p.SetLastTick(order.GetTickNumber())
p.SetLastTickTime(time.Now())
}