ecs/servers/game/logic/player.go

406 lines
13 KiB
Go

package logic
import (
"ecs/proto"
"ecs/proto/old"
"ecs/proto/pb"
"fmt"
"github.com/oylshe1314/framework/errors"
"github.com/oylshe1314/framework/net"
"github.com/oylshe1314/framework/util"
"reflect"
"sort"
"strconv"
"strings"
"sync"
)
const TablePlayer = "player"
type Player struct {
//Unchangeable
RoleId uint64 `bson:"_id"`
Platform uint32 `bson:"platform"`
Channel uint32 `bson:"channel"`
UserId uint64 `bson:"user_id"`
ServerId uint32 `bson:"server_id"`
Username string `bson:"username"`
CreateTime int64 `bson:"create_time"`
//Fields
Language uint32 `bson:"language"`
RoleName string `bson:"role_name"`
RoleGender uint32 `bson:"role_gender"`
AvatarFrame uint32 `bson:"avatar_frame"`
ChatBubble uint32 `bson:"chat_bubble"`
NamePrefix uint32 `bson:"name_prefix"`
NameTitle uint32 `bson:"name_title"`
RoleTitle uint32 `bson:"role_title"`
//RoleExp uint64 `bson:"role_exp"`
//RoleLevel uint32 `bson:"role_level"`
//Fields
LoginTime int64 `bson:"login_time"`
LogoutTime int64 `bson:"logout_time"`
//ChargeTime int64 `bson:"charge_time"`
BanningTime int64 `bson:"banning_time"`
//Fields
LoginDays uint32 `bson:"login_days"`
RefreshTime int64 `bson:"refresh_time"`
PowerNextTime int64 `bson:"power_next_time"`
//CopyMainSceneId uint32 `bson:"copy_main_scene_id"`
//BattlePassSeason uint32 `bson:"battle_pass_season"`
//Fields
RoleHero *PlayerHero `bson:"role_hero"`
//LoginReward *PlayerLoginReward `bson:"login_reward"`
//IdleBattle *PlayerIdleBattle `bson:"idle_battle"`
//Arena *PlayerArena `bson:"arena"`
////Arrays
//BattlePassReward []*PlayerBattlePassReward `bson:"battle_pass_reward"`
//
////Models
//Achievement map[uint64]*PlayerAchievement `bson:"achievement"`
//BattlePass map[uint32]*PlayerBattlePass `bson:"battle_pass"`
//CdKey map[string]*PlayerCdKey `bson:"cd_key"`
CopyPassed map[uint32]*PlayerCopyPassed `bson:"copy_passed"`
//CopySpeed map[uint32]*PlayerCopySpeed `bson:"copy_speed"`
CopyStatus map[uint64]*PlayerCopyStatus `bson:"copy_status"`
Artifact map[uint64]*PlayerArtifact `bson:"artifact"`
Counter map[uint32]map[uint64]*PlayerCounter `bson:"counter"`
Hero map[uint64]*PlayerHero `bson:"hero"`
HeroBook map[uint32]*PlayerHeroBook `bson:"hero_book"`
Item map[uint32]*PlayerItem `bson:"item"`
Equip map[uint64]*PlayerEquip `bson:"rig_equip"`
//GiftPack map[uint32]*PlayerGiftPack `bson:"gift_pack"`
Lineup map[uint64]*PlayerLineup `bson:"lineup"`
//Mail map[uint64]*PlayerMail `bson:"mail"`
Money map[uint32]*PlayerMoney `bson:"money"`
//MonthlyCard map[uint32]*PlayerMonthlyCard `bson:"monthly_card"`
//Planet map[uint32]*PlayerPlanet `bson:"planet"`
//RawStone map[uint32]*PlayerRawStone `bson:"raw_stone"`
//RigCharacter map[uint32]*PlayerRigCharacter `bson:"rig_character"`
//RigComponent map[uint32]*PlayerRigComponent `bson:"rig_component"`
//RigCore map[uint32]*PlayerRigCore `bson:"rig_core"`
//RigEquip map[uint32]*PlayerRigEquip `bson:"rig_equip"`
//RigPendant map[uint32]*PlayerRigPendant `bson:"rig_pendant"`
//RigRemains map[uint32]*PlayerRigRemains `bson:"rig_remains"`
//RigSmelter map[uint32]*PlayerRigSmelter `bson:"rig_smelter"`
//RigTrammels map[uint32]*PlayerRigTrammels `bson:"rig_trammels"`
//RigWarship map[uint32]*PlayerRigWarship `bson:"rig_warship"`
//RoleTalent map[uint32]*PlayerRoleTalent `bson:"role_talent"`
//StorePool map[uint32]*PlayerStorePool `bson:"store_pool"`
//Task map[uint32]*PlayerTask `bson:"task"`
//TaskActive map[uint32]*PlayerTaskActive `bson:"task_active"`
Treasure map[uint64]*PlayerTreasure `bson:"treasures"`
//Temporary
Temp PlayerTemp `bson:"-" json:"-"`
//Non-data
conn *net.Conn
mutex sync.Mutex
exited bool
lostTime int64
lostFunc func()
save map[string]interface{}
wipe map[string]interface{}
manager *PlayerManager
}
func (this *Player) Uid() uint64 {
return this.RoleId
}
func (this *Player) Send(modId, msgId uint16, msg interface{}) (err error) {
if this.conn == nil {
return nil
}
return this.conn.Send(modId, msgId, msg)
}
func (this *Player) TipNotice(err proto.TipError) error {
//return this.Send(proto.ModIdCommon, proto.MsgIdTipNotice, &proto.MsgTipNoticeAck{Message: this.manager.tables.TipNotice.GetLang(err.Error(), int(this.Role.Language))})
return this.Send(old.ModIdCommon, old.MsgIdTipNotice, &old.MsgTipNoticeAck{Message: err.Msg()})
}
func (this *Player) lock() {
this.mutex.Lock()
}
func (this *Player) unlock() {
this.mutex.Unlock()
}
func (this *Player) sync() {
//Role module
_ = this.Send(uint16(pb.ModId_ModuleRole), uint16(pb.MsgId_ModRoleProperty), this.BuildMsgRolePropertyAck())
_ = this.Send(uint16(pb.ModId_ModuleRole), uint16(pb.MsgId_ModRoleMoneyList), this.BuildMsgMoneyListAck())
_ = this.Send(uint16(pb.ModId_ModuleRole), uint16(pb.MsgId_ModRoleLineupList), this.BuildMsgLineupListAck())
//Level module
_ = this.Send(uint16(pb.ModId_ModuleLevel), uint16(pb.MsgId_ModLevelCopyStatusList), this.BuildMsgCopyStatusListAck())
//_ = this.Send(proto.ModIdLevel, proto.MsgIdCopySpeedStatusList, this.BuildMsgCopySpeedStatusListAck())
//_ = this.Send(proto.ModIdLevel, proto.MsgIdPlanetList, this.BuildMsgPlanetListAck())
//_ = this.Send(proto.ModIdLevel, proto.MsgIdPlanetRawStoneStatus, this.BuildMsgPlanetRawStoneStatusListAck())
//_ = this.Send(proto.ModIdLevel, proto.MsgIdCopyArenaStatus, this.BuildMsgCopyArenaStatusAck())
////Battle module
//_ = this.Send(proto.ModIdBattle, proto.MsgIdIdleBattleStatus, this.BuildMsgIdleBattleStatusAck())
//Hero module
_ = this.Send(uint16(pb.ModId_ModuleHero), uint16(pb.MsgId_ModHeroList), this.BuildMsgHeroListAck())
_ = this.Send(uint16(pb.ModId_ModuleHero), uint16(pb.MsgId_ModHeroBookList), this.BuildMsgHeroBookListAck())
//_ = this.Send(proto.ModIdRig, proto.MsgIdRigComponentList, this.BuildMsgRigComponentListAck())
//_ = this.Send(proto.ModIdRig, proto.MsgIdRigEquipList, this.BuildMsgRigEquipListAck())
//_ = this.Send(proto.ModIdRig, proto.MsgIdRigRemainsList, this.BuildMsgRigRemainsListAck())
//_ = this.Send(proto.ModIdRig, proto.MsgIdRigSmelterList, this.BuildMsgRigSmelterListAck())
//_ = this.Send(proto.ModIdRig, proto.MsgIdRigCharacterList, this.BuildMsgRigCharacterListAck())
//_ = this.Send(proto.ModIdRig, proto.MsgIdRigTeamList, this.BuildMsgRigTeamListAck())
//_ = this.Send(proto.ModIdRig, proto.MsgIdRigPendantList, this.BuildMsgRigPendantListAck())
//_ = this.Send(proto.ModIdRig, proto.MsgIdRigCoreList, this.BuildMsgRigCoreListAck())
//_ = this.Send(proto.ModIdRig, proto.MsgIdRigTrammelsList, this.BuildMsgRigTrammelsListAck())
//_ = this.Send(proto.ModIdRig, proto.MsgIdRigWarshipList, this.BuildMsgWarshipListAck())
//Item module
_ = this.Send(uint16(pb.ModId_ModuleItem), uint16(pb.MsgId_ModItemList), this.BuildMsgItemListAck())
_ = this.Send(uint16(pb.ModId_ModuleItem), uint16(pb.MsgId_ModItemEquipList), this.BuildMsgEquipListAck())
_ = this.Send(uint16(pb.ModId_ModuleItem), uint16(pb.MsgId_ModItemTreasureList), this.BuildMsgTreasureListAck())
//Counter module
_ = this.Send(uint16(pb.ModId_ModuleCounter), uint16(pb.MsgId_ModCounterList), this.BuildMsgCounterListAck())
////Mail module
//_ = this.Send(proto.ModIdMail, proto.MsgIdMailList, this.BuildMsgMailListAck())
////Achievement module
//_ = this.Send(proto.ModIdAchievement, proto.MsgIdAchievementList, this.BuildMsgAchievementListAck())
////Task module
//_ = this.Send(proto.ModIdTask, proto.MsgIdTaskList, this.BuildMsgTaskListAck())
//_ = this.Send(proto.ModIdTask, proto.MsgIdActiveInfo, this.BuildMsgActiveInfoAck())
////Store module
//_ = this.Send(proto.ModIdStore, proto.MsgIdStorePoolList, this.BuildMsgStorePoolListAck())
//_ = this.Send(proto.ModIdStore, proto.MsgIdMonthlyCardList, this.BuildMsgMonthlyCardListAck())
//_ = this.Send(proto.ModIdStore, proto.MsgIdBattlePassInfo, this.BuildMsgBattlePassInfoAck())
//_ = this.Send(proto.ModIdStore, proto.MsgIdBattlePassRewardStatusList, this.BuildMsgBattlePassRewardListAck())
//_ = this.Send(proto.ModIdStore, proto.MsgIdLimitGiftPackList, this.BuildMsgLimitGiftPackListAck())
////Activity module
//_ = this.Send(proto.ModIdActivity, proto.MsgIdLoginRewardInfo, this.BuildMsgLoginRewardInfoAck())
}
func (this *Player) enter(conn *net.Conn) {
var cur = NewGameTime(-1)
var pre = NewGameTime(this.RefreshTime)
var firstLogin = this.LoginTime == 0
this.exited = false
this.lostTime = 0
this.lostFunc = nil
this.checkCorrect(firstLogin, cur)
this.checkRefresh(true, pre, cur)
this.conn = conn
this.LoginTime = cur.Timestamp
this.LogoutTime = cur.Timestamp
this.SaveField("login_time", this.LoginTime)
this.SaveField("logout_time", this.LogoutTime)
this.sync()
//if firstLogin {
// var chapterTable = this.manager.tables.CopyChapterExtend.GetFirstBattle()
// if chapterTable != nil {
// //Enter the battle scene of chapter 1 if its the first time login
// this.enterScene(uint32(chapterTable.SceneId), true)
// }
// return
//} else {
// //Enter the main scene
// this.enterScene(1, true)
//}
this.enterScene(1)
}
func (this *Player) reenter(conn *net.Conn) {
this.conn = conn
this.exited = false
this.lostTime = 0
this.lostFunc = nil
this.sync()
this.enterScene(1)
}
func (this *Player) tick(now, elapsed int64) {
this.checkRefresh(false, NewGameTime(this.RefreshTime), NewGameTime(now))
this.checkLostTimeout(now)
this.checkExited(now)
}
func (this *Player) checkLostTimeout(now int64) {
if this.lostTime == 0 {
return
}
if now-this.lostTime < lostTimeout {
return
}
if this.lostFunc != nil {
this.lostFunc()
}
this.exit()
}
func (this *Player) checkExited(now int64) {
if !this.exited {
return
}
this.LogoutTime = now
this.SaveField("logout_time", this.LogoutTime)
}
func (this *Player) lost(timeoutFunc func()) {
this.conn = nil
this.lostFunc = timeoutFunc
this.lostTime = util.Unix()
}
func (this *Player) exit() {
this.exited = true
if this.conn != nil {
_ = this.conn.Close()
this.conn = nil
}
this.lostTime = 0
this.lostFunc = nil
}
func (this *Player) kick(message string) {
_ = this.Send(uint16(pb.ModId_ModuleLogin), uint16(pb.MsgId_ModLoginKickOut), &pb.KickOutAck{Message: message})
this.exit()
}
func (this *Player) ban(banningTime int64) {
this.BanningTime = banningTime
this.SaveField("banning_time", this.BanningTime)
}
func (this *Player) unban() {
this.BanningTime = 0
this.SaveField("banning_time", this.BanningTime)
}
type modelKey struct {
index int
value any
}
func (this *Player) modelKey(model interface{}) (string, error) {
var mv = reflect.ValueOf(model)
if mv.Kind() == reflect.Pointer {
if mv.IsNil() {
return "", errors.Error("the model to be saved could not be nil")
}
mv = mv.Elem()
}
if mv.Kind() != reflect.Struct {
return "", errors.Error("the model to be saved should be a structure")
}
var mt = mv.Type()
var modelName = mt.Name()
if strings.HasPrefix(modelName, "Player") {
modelName = modelName[6:]
}
var keys []*modelKey
var fn = mt.NumField()
for i := 0; i < fn; i++ {
if tag := mt.Field(i).Tag.Get("key"); len(tag) > 0 {
if tag == "-" {
continue
}
keyIndex, err := strconv.Atoi(tag)
if err != nil {
return "", errors.Error("the index of the model key should be a positive integer")
}
keys = append(keys, &modelKey{index: keyIndex, value: mv.Field(i).Interface()})
}
}
if len(keys) == 0 {
return "", errors.Errorf("the structure model '%s' should have a field with the tag 'key'", modelName)
}
sort.Slice(keys, func(i, j int) bool {
return keys[i].index < keys[j].index
})
var sb strings.Builder
sb.WriteString(util.LowerSnakeCase(modelName))
for _, key := range keys {
sb.WriteByte('.')
sb.WriteString(fmt.Sprint(key.value))
}
return sb.String(), nil
}
func (this *Player) SaveField(field string, value interface{}) {
this.save[field] = value
}
func (this *Player) SaveArray(field string, index int, value interface{}) {
this.SaveField(fmt.Sprintf("%s.%d", field, index), value)
}
func (this *Player) SaveModel(model interface{}, fields ...string) {
key, err := this.modelKey(model)
if err != nil {
this.manager.logger.Error("Failed to get the key of the model that will be saved, ", err)
return
}
this.SaveField(key, model)
}
func (this *Player) WipeField(field string, value interface{}) {
this.wipe[field] = value
}
func (this *Player) WipeArray(field string, index int, value interface{}) {
this.WipeField(fmt.Sprintf("%s.%d", field, index), value)
}
func (this *Player) WipeModel(model interface{}) {
key, err := this.modelKey(model)
if err != nil {
this.manager.logger.Error("Failed to get the key of the model that will be wiped", err)
return
}
this.WipeField(key, model)
}