-
Notifications
You must be signed in to change notification settings - Fork 459
/
Space.go
326 lines (267 loc) · 7.93 KB
/
Space.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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
package entity
import (
"fmt"
"github.com/xiaonanln/go-aoi"
"github.com/xiaonanln/goworld/engine/common"
"github.com/xiaonanln/goworld/engine/consts"
"github.com/xiaonanln/goworld/engine/gwlog"
"github.com/xiaonanln/goworld/engine/gwutils"
)
const (
_SPACE_ENTITY_TYPE = "__space__"
_SPACE_KIND_ATTR_KEY = "_K"
_SPACE_ENABLE_AOI_KEY = "_EnableAOI"
_DEFAULT_AOI_DISTANCE = 100
)
var (
nilSpace *Space
)
// Space is the entity type of spaces
//
// Spaces are also entities but with space management logics
type Space struct {
Entity
entities EntitySet
Kind int
I ISpace
aoiMgr aoi.AOIManager
}
func (space *Space) String() string {
if space == nil {
return "nil"
}
if space.Kind != 0 {
return fmt.Sprintf("Space<%d|%s>", space.Kind, space.ID)
} else {
return fmt.Sprintf("NilSpace<%s>", space.ID)
}
}
func (space *Space) DescribeEntityType(desc *EntityTypeDesc) {
desc.DefineAttr(_SPACE_KIND_ATTR_KEY, "AllClients")
}
func (space *Space) GetSpaceRange() (minX, minY, maxX, maxY Coord) {
return -1000, -1000, 1000, 1000
}
func (space *Space) GetTowerRange() (minX, minY, maxX, maxY Coord) {
return -1000, -1000, 1000, 1000
}
// OnInit initialize Space entity
func (space *Space) OnInit() {
space.entities = EntitySet{}
space.I = space.Entity.I.(ISpace)
space.I.OnSpaceInit()
//space.callCompositiveMethod("OnSpaceInit")
}
// OnSpaceInit is a compositive method for initializing space fields
func (space *Space) OnSpaceInit() {
}
// OnCreated is called when Space entity is created
func (space *Space) OnCreated() {
//dispatcher_client.GetDispatcherClientForSend().SendNotifyCreateEntity(space.ID)
space.onSpaceCreated()
if space.IsNil() {
gwlog.Infof("nil space is created: %s, all games connected: %v", space, allGamesConnected)
if allGamesConnected {
space.I.OnGameReady()
}
return
}
if consts.DEBUG_SPACES {
gwlog.Debugf("%s.OnCreated", space)
}
space.I.OnSpaceCreated()
//space.callCompositiveMethod("OnSpaceCreated")
}
func (space *Space) EnableAOI() {
if space.aoiMgr != nil {
return
}
if len(space.entities) > 0 {
gwlog.Panicf("%s is already using AOI", space)
}
space.Attrs.SetBool(_SPACE_ENABLE_AOI_KEY, true)
space.aoiMgr = aoi.NewXZListAOIManager()
//space.aoiMgr = aoi.NewTowerAOIManager(-500, 500, -500, 500, 10)
}
//func (space *Space) UseTowerAOI(minX, maxX, minY, maxY Coord, towerRange Coord) {
// if space.aoiMgr != nil || len(space.entities) > 0 {
// gwlog.Panicf("%s is already using AOI", space)
// }
//
// space.aoiMgr = aoi.NewTowerAOIManager(aoi.Coord(minX), aoi.Coord(maxX), aoi.Coord(minY), aoi.Coord(maxY), aoi.Coord(towerRange))
//}
// OnRestored is called when space entity is restored
func (space *Space) OnRestored() {
space.onSpaceCreated()
//gwlog.Debugf("space %s restored: atts=%+v", space, space.Attrs)
if space.GetBool(_SPACE_ENABLE_AOI_KEY) {
space.EnableAOI()
}
}
func (space *Space) onSpaceCreated() {
space.Kind = int(space.GetInt(_SPACE_KIND_ATTR_KEY))
spaceManager.putSpace(space)
if space.Kind == 0 {
if nilSpace != nil {
gwlog.Panicf("duplicate nil space: %s && %s", nilSpace, space)
}
nilSpace = space
nilSpace.Space = nilSpace
gwlog.Infof("Created nil space: %s", nilSpace)
return
}
}
// OnSpaceCreated is called when space is created
//
// Custom space type can override to provide custom logic
func (space *Space) OnSpaceCreated() {
if consts.DEBUG_SPACES {
gwlog.Debugf("Space %s created", space)
}
}
// OnDestroy is called when Space entity is destroyed
func (space *Space) OnDestroy() {
space.I.OnSpaceDestroy()
//space.callCompositiveMethod("OnSpaceDestroy")
// destroy all entities
for e := range space.entities {
e.Destroy()
}
spaceManager.delSpace(space.ID)
}
// OnSpaceDestroy is called when space is destroying
//
// Custom space type can override to provide custom logic
func (space *Space) OnSpaceDestroy() {
if consts.DEBUG_SPACES {
gwlog.Debugf("Space %s created", space)
}
}
// IsNil checks if the space is the nil space
func (space *Space) IsNil() bool {
return space.Kind == 0
}
// CreateEntity creates a new local entity in this space
func (space *Space) CreateEntity(typeName string, pos Vector3) {
createEntity(typeName, space, pos, "", nil)
}
// LoadEntity loads a entity of specified entityID to the space
//
// If the entity already exists on server, this call has no effect
func (space *Space) LoadEntity(typeName string, entityID common.EntityID, pos Vector3) {
loadEntityLocally(typeName, entityID, space, pos)
}
func (space *Space) enter(entity *Entity, pos Vector3, isRestore bool) {
if consts.DEBUG_SPACES {
gwlog.Debugf("%s.enter <<< %s, avatar count=%d, monster count=%d", space, entity, space.CountEntities("Avatar"), space.CountEntities("Monster"))
}
if entity.Space != nilSpace {
gwlog.Panicf("%s.enter(%s): current space is not nil, but %s", space, entity, entity.Space)
}
if space.IsNil() { // enter nil space does nothing
return
}
entity.Space = space
space.entities.Add(entity)
entity.Position = pos
entity.syncInfoFlag |= sifSyncOwnClient | sifSyncNeighborClients
if !isRestore {
entity.client.sendCreateEntity(&space.Entity, false) // create Space entity before every other entities
if space.aoiMgr != nil && entity.IsUseAOI() {
space.aoiMgr.Enter(&entity.aoi, aoi.Coord(pos.X), aoi.Coord(pos.Z))
}
gwutils.RunPanicless(func() {
space.I.OnEntityEnterSpace(entity)
entity.I.OnEnterSpace()
})
} else {
// restoring ...
if space.aoiMgr != nil && entity.IsUseAOI() {
space.aoiMgr.Enter(&entity.aoi, aoi.Coord(pos.X), aoi.Coord(pos.Z))
}
}
//space.verifyAOICorrectness(entity)
}
func (space *Space) leave(entity *Entity) {
if entity.Space != space {
gwlog.Panicf("%s.leave(%s): entity is not in this Space", space, entity)
}
if space.IsNil() {
// leaving nil space does nothing
return
}
// remove from Space entities
space.entities.Del(entity)
entity.Space = nilSpace
if space.aoiMgr != nil && entity.IsUseAOI() {
space.aoiMgr.Leave(&entity.aoi)
}
entity.client.sendDestroyEntity(&space.Entity)
gwutils.RunPanicless(func() {
space.I.OnEntityLeaveSpace(entity)
entity.I.OnLeaveSpace(space)
})
}
func (space *Space) move(entity *Entity, newPos Vector3) {
if space.aoiMgr == nil {
return
}
entity.Position = newPos
space.aoiMgr.Moved(&entity.aoi, aoi.Coord(newPos.X), aoi.Coord(newPos.Z))
gwlog.Debugf("%s: %s move to %v", space, entity, newPos)
}
// OnEntityEnterSpace is called when entity enters space
//
// Custom space type can override this function
func (space *Space) OnEntityEnterSpace(entity *Entity) {
if consts.DEBUG_SPACES {
gwlog.Debugf("%s ENTER SPACE %s", entity, space)
}
}
// OnEntityLeaveSpace is called when entity leaves space
//
// Custom space type can override this function
func (space *Space) OnEntityLeaveSpace(entity *Entity) {
if consts.DEBUG_SPACES {
gwlog.Debugf("%s LEAVE SPACE %s", entity, space)
}
}
// CountEntities returns the number of entities of specified type in space
func (space *Space) CountEntities(typeName string) int {
count := 0
for e := range space.entities {
if e.TypeName == typeName {
count += 1
}
}
return count
}
// GetEntityCount returns the total count of entities in space
func (space *Space) GetEntityCount() int {
return len(space.entities)
}
// ForEachEntity visits all entities in space and call function f with each entity
func (space *Space) ForEachEntity(f func(e *Entity)) {
for e := range space.entities {
f(e)
}
}
// GetEntity returns the entity in space with specified ID, nil otherwise
func (space *Space) GetEntity(entityID common.EntityID) *Entity {
entity := GetEntity(entityID)
if entity == nil {
return nil
}
if space.entities.Contains(entity) {
return entity
} else {
return nil
}
}
// aoi Management
func (space *Space) addToAOI(entity *Entity) {
}
// OnGameReady is called when the game server is ready on NilSpace only
func (space *Space) OnGameReady() {
gwlog.Warnf("Game server is ready. Override function %T.OnGameReady to write your own game logic!", space.I)
}