| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- package handler
- import (
- "encoding/json"
- "fmt"
- "runtime/debug"
- "sync"
- "time"
- "bet24.com/log"
- "bet24.com/servers/common"
- item "bet24.com/servers/micros/item_inventory/proto"
- cash "bet24.com/servers/micros/money/proto"
- chip "bet24.com/servers/micros/money/proto"
- notification "bet24.com/servers/micros/notification/proto"
- userservices "bet24.com/servers/micros/userservices/proto"
- vipservice "bet24.com/servers/micros/userservices/proto"
- )
- const (
- Item_ID_Lottery = 4 //抽奖券
- )
- type user_inventory struct {
- lock *sync.RWMutex
- userId int
- ipAddress string
- userItemList map[int]*item.UserItem
- endChan chan int
- }
- // InventoryAction
- const (
- Inventory_Consume = iota // 使用 0
- Inventory_Add // 增加 1
- Inventory_Expire // 到期 2
- Inventory_Update // 数据有变化 3
- Inventory_Sell // 出售 4
- )
- func newUserInventory(userId int, ipAddress string) *user_inventory {
- ret := new(user_inventory)
- ret.lock = &sync.RWMutex{}
- ret.userItemList = make(map[int]*item.UserItem)
- ret.userId = userId
- ret.ipAddress = ipAddress
- ret.endChan = make(chan int)
- //TODO: 去数据库取自己的背包道具
- ret.loadUserInventory()
- // 遍历一下是否有时效道具,使用掉
- for _, v := range ret.userItemList {
- sysItem := getItemManager().getItem(v.ItemId)
- if sysItem == nil {
- continue
- }
- if sysItem.Duration == 0 {
- continue
- }
- if sysItem.ActiveId == 0 || sysItem.ActiveId == v.ItemId {
- continue
- }
- go ret.consume(v.ItemId, 0, 0, 0, 0)
- }
- userTicker := time.NewTicker(time.Duration(5) * time.Second)
- go func(t *time.Ticker) {
- for { //循环
- select {
- case <-ret.endChan:
- t.Stop()
- return
- case <-t.C:
- ret.Timer_checkExpire()
- }
- }
- }(userTicker)
- return ret
- }
- func (this *user_inventory) loadUserInventory() {
- userItems := getUserItemList(this.userId)
- this.lock.Lock()
- this.userItemList = userItems
- /*for _, v := range userItems {
- // 如果是永久道具,直接使用
- if v.Duration == -1 {
- go this.consume(v.ItemId, 0, 1, 0, 0)
- }
- }*/
- this.lock.Unlock()
- }
- // 析构,停止计时器等
- func (this *user_inventory) destructor() {
- close(this.endChan)
- }
- func (this *user_inventory) Timer_checkExpire() {
- //检查道具是否时效
- var toRemove []*item.UserItem
- this.lock.RLock()
- for _, u := range this.userItemList {
- if this.IsExpired(u) {
- log.Debug("user_inventory.Timer_checkExpire userId=%d ==> %+v", this.userId, u)
- // 如果是装扮道具,通知一下用户信息那边修改
- sysItem := getItemManager().getItem(u.ItemId)
- if sysItem == nil {
- log.Debug("user_inventory.Timer_checkExpire itemId[%d] invalid", u.ItemId)
- toRemove = append(toRemove, u)
- continue
- }
- if sysItem.Type == item.Item_Decoration {
- userservices.OnDecorationExpired(this.userId, u.ItemId)
- }
- //道具时效, 通知客户端
- d, _ := json.Marshal(notification.NotificationInventory{ItemIds: []int{u.ItemId}, Action: Inventory_Expire})
- go notification.AddNotification(this.userId, notification.Notification_Inventory, string(d))
- //删除道具
- toRemove = append(toRemove, u)
- }
- }
- this.lock.RUnlock()
- if len(toRemove) <= 0 {
- return
- }
- this.lock.Lock()
- for _, v := range toRemove {
- //TODO:写道具日志
- go this.addLog(v.ItemId, v.Count, -v.Count, 0, 0, "道具过期", this.ipAddress)
- //TODO:通知数据库道具过期
- delUserItem(this.userId, v)
- delete(this.userItemList, v.ItemId)
- }
- this.lock.Unlock()
- }
- func (this *user_inventory) getItemList() []*item.UserItem {
- var l []*item.UserItem
- this.lock.RLock()
- defer this.lock.RUnlock()
- for _, v := range this.userItemList {
- l = append(l, v)
- }
- return l
- }
- func (this *user_inventory) getItemCount(itemId int) int {
- this.lock.RLock()
- defer this.lock.RUnlock()
- for _, v := range this.userItemList {
- if v.ItemId == itemId {
- return v.Count
- }
- }
- return 0
- }
- // 获取抽奖券
- func (this *user_inventory) getLottery() *item.UserItem {
- this.lock.RLock()
- defer this.lock.RUnlock()
- for _, v := range this.userItemList {
- if v.ItemId == Item_ID_Lottery {
- return v
- }
- }
- return nil
- }
- // 消耗抽奖券
- func (this *user_inventory) consumeLottery(count int) (bool, string) {
- return this.consume(Item_ID_Lottery, 0, count, 0, 0)
- }
- func (this *user_inventory) consumeBulk(items []item.ItemPack, logType int) bool {
- var rollBackItems []item.ItemPack
- allOk := true
- for _, v := range items {
- ok, _ := this.consume(v.ItemId, 0, v.Count, logType, 0)
- if ok {
- rollBackItems = append(rollBackItems, v)
- } else {
- allOk = false
- break
- }
- }
- if !allOk {
- for _, v := range rollBackItems {
- this.addItem(v, logType)
- }
- }
- return allOk
- }
- func (this *user_inventory) consume(itemId int, bullet int, count int, logType int, isGift int) (bool, string) {
- // log.Debug("user_inventory.consume userId=%d itemId=%d bullet=%d count=%d logType=%d isGift=%d",
- // this.userId, itemId, bullet, count, logType, isGift)
- sysItem := getItemManager().getItem(itemId)
- if sysItem == nil {
- log.Debug("user_inventory.consume itemId[%d] invalid", itemId)
- return false, "无效的道具ID"
- }
- // 非背包展示道具,直接调数据库接口更新
- if sysItem.Type == item.Item_Gold || sysItem.Type == item.Item_Chip || sysItem.Type == item.Item_Vitality {
- // TODO: 通知数据库刷新
- ok := false
- errMsg := ""
- switch sysItem.Type {
- case item.Item_Gold:
- ok = cash.ReduceMoney(this.userId, count, logType, "道具模块", "扣金币", this.ipAddress)
- if !ok {
- errMsg = "金币不足"
- }
- case item.Item_Chip:
- ok = chip.ReduceChip(this.userId, count, logType, "道具模块", "扣筹码", this.ipAddress) == 1
- if !ok {
- errMsg = "筹码不足"
- }
- }
- return ok, errMsg
- }
- if count == 0 {
- count = 1
- }
- this.lock.Lock()
- defer this.lock.Unlock()
- // 我有没有这个道具?
- myItem, ok := this.userItemList[itemId]
- if !ok {
- log.Debug("user_inventory.consume itemId[%d] not exist", itemId)
- return false, "道具不存在"
- }
- if myItem.Start > 0 && !this.IsExpired(myItem) {
- // 本身是已使用的道具,这里只是切换成激活状态
- return true, "使用成功"
- }
- if myItem.Count < count {
- log.Debug("user_inventory.consume itemId[%d] not enough %d < %d", itemId, myItem.Count, count)
- return false, "道具数量不够"
- }
- //时效道具, 加时间
- retMsg := "使用成功"
- isDuration := false
- if sysItem.ActiveId > 0 && isGift == 0 {
- isDuration = true
- if l, ok := this.userItemList[sysItem.ActiveId]; ok {
- // 如果本身为永久道具,则折现
- if l.Duration == -1 {
- gold := sysItem.Value * count
- go this.addItem(item.ItemPack{ItemId: item.Item_Gold, Count: gold}, common.LOGTYPE_TOOL_CONVERT)
- retMsg = fmt.Sprintf("道具已折算成[%d]金币", gold)
- } else if myItem.Duration == -1 {
- l.Duration = -1
- } else {
- // 根据个数确定时长
- l.Duration += myItem.Duration * count
- }
- } else {
- //新增使用中的时效道具
- ui := &item.UserItem{
- ItemId: sysItem.ActiveId,
- Count: 1,
- Start: common.GetTimeStamp(),
- Duration: myItem.Duration * count,
- }
- this.userItemList[sysItem.ActiveId] = ui
- }
- go updateUserItem(this.userId, this.userItemList[sysItem.ActiveId])
- }
- // 如果是兑换券
- if sysItem.Type == item.Item_GiftCard {
- ok, retMsg = getGiftCardManager().useCard(this.userId, sysItem.Id)
- // log.Debug("user_inventory.consume.useCard userId=%d itemId=%d ret=%v retMsg=%s", this.userId, sysItem.Id, ok, retMsg)
- if !ok {
- return ok, retMsg
- }
- }
- currCount := myItem.Count
- myItem.Count -= count
- remark := "使用道具" + common.GetLogTypeName(bullet)
- go this.addLog(itemId, currCount, -count, myItem.Count, 0, remark, this.ipAddress)
- //道具用完了, 删除
- if myItem.Count == 0 {
- //TODO:通知数据库道具删除
- go delUserItem(this.userId, myItem)
- delete(this.userItemList, itemId)
- } else {
- //TODO:通知数据库道具消耗
- go updateUserItem(this.userId, myItem)
- }
- //通知客户端
- if !isDuration {
- d, _ := json.Marshal(notification.NotificationInventory{ItemIds: []int{itemId}, Action: Inventory_Consume})
- go notification.AddNotification(this.userId, notification.Notification_Inventory, string(d))
- }
- return true, retMsg
- }
- func (this *user_inventory) sell(itemId, count, logType int) (bool, string) {
- log.Debug("user_inventory.sell userId=%d itemId=%d count=%d logType=%d",
- this.userId, itemId, count, logType)
- sysItem := getItemManager().getItem(itemId)
- if sysItem == nil {
- log.Debug("user_inventory.sell itemId[%d] invalid", itemId)
- return false, "无效的道具ID"
- }
- if sysItem.Value <= 0 {
- log.Debug("user_inventory.sell itemId[%d] is not sell", itemId)
- return false, "该道具无法出售"
- }
- if sysItem.ShowPrice <= 0 {
- return false, "该道具无法出售"
- }
- if logType <= 0 {
- logType = common.LOGTYPE_TOOL_SELL
- }
- if count == 0 {
- count = 1
- }
- this.lock.Lock()
- defer this.lock.Unlock()
- // 我有没有这个道具?
- myItem, ok := this.userItemList[itemId]
- if !ok {
- log.Debug("user_inventory.sell itemId[%d] not exist", itemId)
- return false, "道具不存在"
- }
- if myItem.Count <= 0 {
- log.Debug("user_inventory.sell itemId[%d] is zero", itemId)
- return false, "道具不存在"
- }
- if myItem.Count < count {
- return false, "道具数量不够"
- }
- currCount := myItem.Count
- myItem.Count -= count
- gold := sysItem.Value * count
- go this.addItem(item.ItemPack{ItemId: item.Item_Gold, Count: gold}, logType)
- retMsg := fmt.Sprintf("道具已折算成[%d]金币", gold)
- go this.addLog(itemId, currCount, -count, myItem.Count, logType, "扣减", this.ipAddress)
- // 道具用完了, 删除
- if myItem.Count == 0 {
- // TODO:通知数据库道具删除
- go delUserItem(this.userId, myItem)
- delete(this.userItemList, itemId)
- } else {
- // TODO:通知数据库道具消耗
- go updateUserItem(this.userId, myItem)
- }
- // 通知客户端
- d, _ := json.Marshal(notification.NotificationInventory{ItemIds: []int{itemId}, Action: Inventory_Sell})
- go notification.AddNotification(this.userId, notification.Notification_Inventory, string(d))
- return true, retMsg
- }
- func (this *user_inventory) addLog(itemId, currCount, wantCount, stillCount, logType int, remark, ipAddress string) {
- // log.Debug("user_inventory addLog %d:%d currCount=%d wantCount=%d stillCount=%d logType=%d remark=%s",
- // this.userId, itemId, currCount, wantCount, stillCount, logType, remark)
- remark = common.GetLogTypeName(logType) + remark
- go addLog(this.userId, itemId, currCount, wantCount, stillCount, remark, ipAddress)
- }
- func (this *user_inventory) addItemsWithExpireTime(items []item.ItemPack, logType int, expireTime int) bool {
- // 特殊接口,只处理时效道具
- now := int(time.Now().Unix())
- if expireTime < now {
- log.Error("user_inventory.addItemsWithExpireTime userId[%d] expireTime[%d] < now[%d]", this.userId, expireTime, now)
- return false
- }
- for _, v := range items {
- sysItem := getItemManager().getItem(v.ItemId)
- if sysItem == nil {
- log.Error("user_inventory.addItemsWithExpireTime invalid userId[%d] ItemId %d", this.userId, v.ItemId)
- continue
- }
- itemId := v.ItemId
- if sysItem.ActiveId != 0 {
- itemId = sysItem.ActiveId
- }
- // 我本身有没有
- this.lock.Lock()
- myItem, ok := this.userItemList[itemId]
- if ok {
- myItem.Start = now
- myItem.Duration = expireTime - now
- } else {
- this.userItemList[itemId] = &item.UserItem{ItemId: itemId, Start: now, Duration: expireTime - now, Count: 1}
- myItem = this.userItemList[itemId]
- }
- this.lock.Unlock()
- //TODO: 写道具日志
- go this.addLog(myItem.ItemId, 0, v.Count, myItem.Count, logType, "添加道具", this.ipAddress)
- //TODO: 通知数据库更新道具信息
- go updateUserItem(this.userId, myItem)
- }
- return true
- }
- func (this *user_inventory) addItem(it item.ItemPack, logType int) bool {
- // log.Debug("user_inventory.addItem userId=%d ipAddress=%s it=%+v", this.userId, this.ipAddress, it)
- if it.Count <= 0 {
- log.Debug("user_inventory.addItem userId[%d] %v", this.userId, it)
- log.Debug("%s", debug.Stack())
- return false
- }
- s := getItemManager().getItem(it.ItemId)
- if s == nil {
- log.Error("user_inventory.addItem invalid userId[%d] ItemId %d", this.userId, it.ItemId)
- return false
- }
- //log.Debug("user_inventory.addItem UserId[%d] sys[%+v]", this.userId, s)
- // 非背包展示道具,直接调数据库接口更新
- if s.Type == item.Item_Gold || s.Type == item.Item_Chip || s.Type == item.Item_Vitality || s.Type == item.Item_Vip {
- // TODO: 通知数据库刷新
- switch s.Type {
- case item.Item_Gold:
- cash.GiveMoney(this.userId, it.Count, logType, "道具模块", "加金币", this.ipAddress)
- case item.Item_Chip:
- chip.GiveChip(this.userId, it.Count, logType, "道具模块", "加筹码", this.ipAddress)
- case item.Item_Vip:
- // 添加vip时长
- log.Debug("user_inventory.addItem UserId[%d] AddVipSeconds %d", this.userId, s.Duration)
- vipservice.AddVipSeconds(this.userId, s.Duration)
- }
- return true
- } else if s.Type == item.Item_Addminutes || s.Type == item.Item_Physical || s.Type == item.Item_Cash { //实物
- return true
- }
- // 如果是虚拟道具(限时道具本体),不添加
- if getItemManager().isVirtualItem(it.ItemId) {
- log.Release("user_inventory.addItem virtual item [%d]", it.ItemId)
- return false
- }
- this.lock.Lock()
- myItem, ok := this.userItemList[it.ItemId]
- if ok {
- currCount := 0
- currCount = myItem.Count
- myItem.Count += it.Count
- this.lock.Unlock()
- //TODO: 写道具日志
- go this.addLog(myItem.ItemId, currCount, it.Count, myItem.Count, logType, "添加道具", this.ipAddress)
- // 如果是兑换券
- if s.Type == item.Item_GiftCard {
- go getGiftCardManager().genCard(this.userId, s.Id, it.Count)
- }
- //TODO: 通知数据库更新道具信息
- updateUserItem(this.userId, myItem)
- return true
- }
- this.lock.Unlock()
- // 如果是时效道具,直接使用
- if s.Duration != 0 {
- this.lock.Lock()
- this.userItemList[it.ItemId] = &item.UserItem{ItemId: s.Id, Start: 0, Duration: s.Duration, Count: it.Count}
- this.lock.Unlock()
- //TODO: 写道具日志
- this.addLog(it.ItemId, 0, it.Count, 0, logType, "新增道具", this.ipAddress)
- this.consume(it.ItemId, 0, it.Count, 0, 0)
- return true
- }
- //玩家还没有该道具,新增
- ui := &item.UserItem{
- ItemId: s.Id,
- Start: 0,
- Duration: s.Duration,
- }
- ui.Count = it.Count
- this.lock.Lock()
- this.userItemList[ui.ItemId] = ui
- this.lock.Unlock()
- //TODO: 写道具日志
- this.addLog(ui.ItemId, 0, it.Count, ui.Count, logType, "新增道具", this.ipAddress)
- // 如果是兑换券
- if s.Type == item.Item_GiftCard {
- go getGiftCardManager().genCard(this.userId, s.Id, it.Count)
- }
- //TODO: 通知数据库更新道具信息
- go updateUserItem(this.userId, ui)
- return true
- }
- func (this *user_inventory) IsExpired(u *item.UserItem) bool {
- if u.Duration <= 0 {
- return false
- }
- if u.Start == 0 || u.Duration == 0 {
- return false
- }
- now := common.GetTimeStamp()
- return (now - u.Start) > u.Duration
- }
- func (this *user_inventory) reduceItemByAdmin(opUserId int, opUserName string, itemId, count int) (bool, string) {
- if count <= 0 {
- return false, "扣减失败,无效参数"
- }
- sysItem := getItemManager().getItem(itemId)
- if sysItem == nil {
- log.Debug("user_inventory.reduceItemByAdmin itemId[%d] invalid", itemId)
- return false, "扣减失败,无效的道具ID"
- }
- this.lock.Lock()
- defer this.lock.Unlock()
- // 我有没有这个道具?
- myItem, ok := this.userItemList[itemId]
- if !ok {
- log.Debug("user_inventory.reduceItemByAdmin itemId[%d] not exist", itemId)
- return false, "扣减失败,道具不存在"
- }
- if myItem.Count <= 0 {
- log.Debug("user_inventory.reduceItemByAdmin itemId[%d] is zero", itemId)
- return false, "扣减失败,道具不存在"
- }
- currCount := myItem.Count
- myItem.Count -= count
- msg := fmt.Sprintf("后台扣减道具(opUserId:%d opUserName:%s)", opUserId, opUserName)
- go this.addLog(itemId, currCount, -count, myItem.Count, 0, msg, this.ipAddress)
- //道具用完了, 删除
- if myItem.Count == 0 {
- //TODO:通知数据库道具删除
- delUserItem(this.userId, myItem)
- delete(this.userItemList, itemId)
- } else {
- //TODO:通知数据库道具消耗
- updateUserItem(this.userId, myItem)
- }
- //通知客户端
- d, _ := json.Marshal(notification.NotificationInventory{ItemIds: []int{itemId}, Action: Inventory_Consume})
- go notification.AddNotification(this.userId, notification.Notification_Inventory, string(d))
- retmsg := fmt.Sprintf("扣减成功,道具id=%d 道具名称=%s 扣减道具数=%d 剩余道具数=%d",
- myItem.ItemId, sysItem.Name, count, myItem.Count)
- return true, retmsg
- }
|