-
Notifications
You must be signed in to change notification settings - Fork 4
/
player.go
253 lines (227 loc) · 7.06 KB
/
player.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
package game
import (
"github.com/fish-tennis/gentity"
"github.com/fish-tennis/gserver/cache"
"github.com/fish-tennis/gserver/internal"
"time"
. "github.com/fish-tennis/gnet"
"github.com/fish-tennis/gserver/db"
"github.com/fish-tennis/gserver/logger"
"github.com/fish-tennis/gserver/pb"
"google.golang.org/protobuf/proto"
)
const (
// Player在redis里的前缀
PlayerCachePrefix = "p"
)
var _ gentity.RoutineEntity = (*Player)(nil)
// 玩家对象
type Player struct {
gentity.BaseRoutineEntity
// 玩家名
name string
// 账号id
accountId int64
// 区服id
regionId int32
//accountName string
// 是否使用网关
useGate bool
// 关联的连接,如果是网关模式,就是网关的连接
// 如果是客户端直连模式,就是客户端连接
connection Connection
}
// 玩家名(unique)
func (this *Player) GetName() string {
return this.name
}
// 账号id
func (this *Player) GetAccountId() int64 {
return this.accountId
}
// 区服id
func (this *Player) GetRegionId() int32 {
return this.regionId
}
// 获取组件
func (this *Player) GetComponent(componentName string) gentity.Component {
index := GetComponentIndex(componentName)
if index >= 0 {
return this.GetComponentByIndex(index)
}
return nil
}
// 玩家数据保存数据库
func (this *Player) SaveDb(removeCacheAfterSaveDb bool) error {
return gentity.SaveEntityChangedDataToDb(db.GetPlayerDb(), this, cache.Get(), removeCacheAfterSaveDb, PlayerCachePrefix)
}
func (this *Player) SaveCache(kvCache gentity.KvCache) error {
return this.BaseEntity.SaveCache(kvCache, PlayerCachePrefix, this.GetId())
}
// 设置关联的连接,支持客户端直连模式和网关模式
func (this *Player) SetConnection(connection Connection, useGate bool) {
this.useGate = useGate
if !useGate {
// 取消之前的连接和该玩家的关联
if this.connection != nil && this.connection != connection {
this.connection.SetTag(nil)
}
// 客户端直连模式,设置连接和玩家的关联
if connection != nil {
connection.SetTag(this.GetId())
}
}
this.connection = connection
}
func (this *Player) ResetConnection() {
this.connection = nil
}
func (this *Player) GetConnection() Connection {
return this.connection
}
func (this *Player) OnDisconnect(connection Connection) {
if this.GetConnection() == connection {
this.ResetConnection()
this.Stop()
logger.Debug("player %v exit", this.GetId())
}
}
// 发包(protobuf)
// NOTE:调用Send(command,message)之后,不要再对message进行读写!
func (this *Player) Send(command PacketCommand, message proto.Message) bool {
if this.connection != nil {
if this.useGate {
// 网关模式,自动附加上playerId
return this.connection.SendPacket(internal.NewGatePacket(this.GetId(), command, message))
} else {
return this.connection.Send(command, message)
}
}
return false
}
// 通用的错误返回消息
func (this *Player) SendErrorRes(errorReqCmd PacketCommand, errorMsg string) bool {
return this.Send(PacketCommand(pb.CmdInner_Cmd_ErrorRes), &pb.ErrorRes{
Command: int32(errorReqCmd),
ResultStr: errorMsg,
})
}
// 分发事件给组件
func (this *Player) FireEvent(event interface{}) {
logger.Debug("%v FireEvent:%v", this.GetId(), event)
// TODO:建一个事件类型和组件的映射表 eventType -> component list
this.RangeComponent(func(component gentity.Component) bool {
if eventReceiver, ok := component.(gentity.EventReceiver); ok {
eventReceiver.OnEvent(event)
}
return true
})
}
// 分发条件相关事件
func (this *Player) FireConditionEvent(event interface{}) {
logger.Debug("%v FireConditionEvent:%v", this.GetId(), event)
this.GetQuest().Quests.OnEvent(event)
this.GetActivities().OnEvent(event)
}
func (this *Player) GetLevel() int32 {
return this.GetBaseInfo().Data.Level
}
// 开启消息处理协程
// 每个玩家一个独立的消息处理协程
// 除了登录消息,其他消息都在玩家自己的协程里处理,因此这里对本玩家的操作不需要加锁
func (this *Player) RunRoutine() bool {
logger.Debug("player RunRoutine %v", this.GetId())
ok := this.RunProcessRoutine(this, &gentity.RoutineEntityRoutineArgs{
EndFunc: func(routineEntity gentity.RoutineEntity) {
// 分发事件:玩家退出游戏
this.FireEvent(&internal.EventPlayerExit{})
// 协程结束的时候,移除玩家
GetPlayerMgr().RemovePlayer(this)
},
ProcessMessageFunc: func(routineEntity gentity.RoutineEntity, message interface{}) {
this.processMessage(message.(*ProtoPacket))
},
AfterTimerExecuteFunc: func(routineEntity gentity.RoutineEntity, t time.Time) {
// 如果有需要保存的数据修改了,即时保存数据库
this.SaveCache(cache.Get())
},
})
if ok {
// 每分钟执行一次,刷新在线时间
this.GetTimerEntries().After(time.Minute, func() time.Duration {
evt := &pb.EventPlayerPropertyInc{
PlayerId: this.GetId(),
PropertyName: "OnlineMinute",
PropertyValue: 1,
}
this.FireEvent(evt)
return time.Minute
})
}
return ok
}
func (this *Player) processMessage(message *ProtoPacket) {
defer func() {
if err := recover(); err != nil {
logger.Error("recover:%v", err)
logger.LogStack()
}
}()
logger.Debug("processMessage %v", proto.MessageName(message.Message()).Name())
// 先找组件接口
if gentity.ProcessComponentHandler(this, message.Command(), message.Message()) {
// 如果有需要保存的数据修改了,即时保存缓存
this.SaveCache(cache.Get())
return
}
// 再找func(player *Player, packet Packet)格式的回调接口
if playerHandler, ok := _playerHandler[message.Command()]; ok {
playerHandler(this, message)
this.SaveCache(cache.Get())
return
}
// 再找func(connection Connection, packet Packet)的回调接口
packetHandler := _clientConnectionHandler.GetPacketHandler(message.Command())
if packetHandler != nil {
packetHandler(this.GetConnection(), message)
// 如果有需要保存的数据修改了,即时保存缓存
this.SaveCache(cache.Get())
return
}
logger.Error("unhandle message:%v", message.Command())
}
// 放入消息队列
func (this *Player) OnRecvPacket(packet *ProtoPacket) {
logger.Debug("OnRecvPacket %v", proto.MessageName(packet.Message()).Name())
this.PushMessage(packet)
}
// 从加载的数据构造出玩家对象
func CreatePlayerFromData(playerData *pb.PlayerData) *Player {
player := &Player{
name: playerData.Name,
accountId: playerData.AccountId,
regionId: playerData.RegionId,
BaseRoutineEntity: *gentity.NewRoutineEntity(32),
}
player.Id = playerData.XId
// 初始化玩家的各个模块
for _, componentCtor := range _playerComponentRegister {
component := componentCtor.Ctor(player, playerData)
if component != nil && player.GetComponentByName(component.GetName()) == nil {
player.AddComponent(component, nil)
}
}
return player
}
func CreateTempPlayer(playerId, accountId int64) *Player {
playerData := &pb.PlayerData{}
player := CreatePlayerFromData(playerData)
player.Id = playerId
player.accountId = accountId
return player
}
func NewEmptyPlayer(playerId int64) *Player {
p := &Player{}
p.Id = playerId
return p
}