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) }