chirpnest/obj.go
2019-06-28 14:43:32 +08:00

750 lines
18 KiB
Go

package main
import (
"errors"
"fmt"
"log"
"regexp"
"strconv"
"sync"
"time"
)
var (
cacheObjCastle *sync.Map
cacheObjGuild *sync.Map
cacheObjUser *sync.Map
cacheObjItem *sync.Map
cacheObjMsg *sync.Map
)
func getObjTypeId(objId int64) (int64, error) {
var objTypeId int64
stmt, err := db.Prepare(`SELECT o.obj_type_id FROM obj o WHERE o.id = ?`)
if err != nil {
return 0, err
}
defer stmt.Close()
err = stmt.QueryRow(objId).Scan(&objTypeId)
if err != nil {
return 0, err
}
return objTypeId, nil
}
func getObjSubTypeId(objId int64) (int64, error) {
var objSubTypeId int64
stmt, err := db.Prepare(`SELECT o.obj_sub_type_id FROM obj o WHERE o.id = ?`)
if err != nil {
return 0, err
}
defer stmt.Close()
err = stmt.QueryRow(objId).Scan(&objSubTypeId)
if err != nil {
return 0, err
}
return objSubTypeId, nil
}
func setObjSubTypeId(objId int64, objSubTypeID64 int64) error {
stmt, err := db.Prepare(`UPDATE obj o SET o.obj_sub_type_id = ? WHERE o.id = ?;`)
logOnError(err, "setObjSubTypeId : prepare update")
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec(objSubTypeID64, objId)
logOnError(err, "setObjSubTypeId : exec update")
return err
}
func objAddName(objID64 int64, name string) error {
stmt, err := db.Prepare(`INSERT INTO obj_name (obj_id, name, priority)
SELECT ? obj_id, ? name, (SELECT MAX(priority) + 1 FROM obj_name WHERE obj_id = ?) priority FROM DUAL;`)
_, err = stmt.Exec(objID64, name, objID64)
return err
}
func addObjMsg(msgID64 int64, msgChatID64 int64, msgUserID64 int64, msgSenderUserID64 int64, msgDate time.Time, msgText string) (int64, error) {
tx, err := db.Begin()
logOnError(err, "addObjMsg : start transaction")
if err != nil {
return 0, err
}
res, err := tx.Exec(`INSERT INTO obj (obj_type_id, obj_sub_type_id)
VALUES (` + strconv.Itoa(objTypeMessage) + `,` + strconv.Itoa(objSubTypeMessageUnknown) + `);`)
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjMsg : rollback insert obj")
return 0, err
}
objId, err := res.LastInsertId()
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjMsg : rollback get lastInsertId")
return 0, err
}
stmt, err := tx.Prepare(`INSERT INTO obj_msg (obj_id, msg_id, chat_id, user_id, sender_user_id, date, text)
VALUES (?, ?, ?, ?, ?, ?, ?);`)
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjMsg : rollback prepare insert obj_msg")
return 0, err
}
defer stmt.Close()
_, err = stmt.Exec(objId, msgID64, msgChatID64, msgUserID64, msgSenderUserID64, msgDate, msgText)
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjMsg : rollback exec insert obj_msg")
return 0, err
}
err = tx.Commit()
if err != nil {
return 0, err
}
m := new(ChatWarsMessage)
m.ObjID64 = objId
m.UserID64 = msgUserID64
m.SenderUserID64 = msgSenderUserID64
m.Date = msgDate
m.ID64 = msgID64
m.ChatID64 = msgChatID64
m.Text = msgText
cacheObjMsg.Store(objId, *m)
return objId, nil
}
func getObjMsg(objId int64) (*ChatWarsMessage, error) {
if v, ok := cacheObjMsg.Load(objId); ok {
m := v.(ChatWarsMessage)
return &m, nil
}
var m *ChatWarsMessage
stmt, err := db.Prepare(`SELECT om.msg_id, om.chat_id, om.user_id, om.sender_user_id, om.date, om.text FROM obj_msg om WHERE om.obj_id = ?`)
if err != nil {
return m, err
}
defer stmt.Close()
m = new(ChatWarsMessage)
err = stmt.QueryRow(objId).Scan(&m.ID64, &m.ChatID64, &m.UserID64, &m.SenderUserID64, &m.Date, &m.Text)
if err != nil {
return m, err
}
cacheObjMsg.Store(objId, *m)
return m, nil
}
func loadObjMsg() error {
cacheObjMsg = new(sync.Map)
return nil
}
func delObj(objId int64) error {
objSubTypeId, err := getObjSubTypeId(objId)
if err != nil {
return err
}
if objSubTypeId != objSubTypeMessageAuctionAnnounce {
return errors.New("Can only delete objSubTypeMessageAuctionAnnounce.")
}
cacheObjMsg.Delete(objId) // better delete from cache before, worst case we reload after
stmt, err := db.Prepare(`DELETE FROM obj WHERE id = ?`)
if err != nil {
return err
}
defer stmt.Close()
res, err := stmt.Exec(objId)
if err != nil {
return err
}
count, err := res.RowsAffected()
if err != nil {
return err
}
if count > 1 {
return errors.New("More than one row impacted.")
} else if count == 0 {
return errors.New("No row impacted.")
}
return nil
}
func addObjCastle(logo string, name string) (int64, error) {
tx, err := db.Begin()
logOnError(err, "addObjCastle : start transaction")
if err != nil {
return 0, err
}
res, err := tx.Exec(`INSERT INTO obj (obj_type_id, obj_sub_type_id)
VALUES (` + strconv.Itoa(objTypeCastle) + `,` + strconv.Itoa(objSubTypeCastle) + `);`)
logOnError(err, "addObjCastle : exec insert obj")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjCastle : rollback insert obj")
return 0, err
}
objId, err := res.LastInsertId()
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjCastle : rollback get lastInsertId")
return 0, err
}
stmt, err := tx.Prepare(`INSERT INTO obj_castle (obj_id, logo, name)
VALUES (?, ?, ?);`)
logOnError(err, "addObjCastle : prepare insert obj_castle")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjCastle : rollback prepare insert obj_castle")
return 0, err
}
defer stmt.Close()
_, err = stmt.Exec(objId, logo, name)
logOnError(err, "addObjCastle : exec insert obj_castle")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjCastle : rollback exec insert obj_castle")
return 0, err
}
err = tx.Commit()
logOnError(err, "addObjCastle : commit")
if err != nil {
return 0, err
}
return objId, nil
}
func getObjCastleID(s string) int64 {
if v, ok := cacheObjCastle.Load(s); ok {
c := v.(ChatWarsCastle)
return c.ObjID64
} else {
v, _ := cacheObjCastle.Load(``)
c := v.(ChatWarsCastle)
return c.ObjID64
}
}
func loadObjCastle() error {
var (
id int64
logo string
name string
)
cacheObjCastle = new(sync.Map)
castles, err := db.Query(`SELECT oc.obj_id, oc.logo, oc.name FROM obj_castle oc;`)
if err != nil {
return err
}
defer castles.Close()
for castles.Next() {
err = castles.Scan(&id, &logo, &name)
if err != nil {
return err
}
c := new(ChatWarsCastle)
c.ObjID64 = id
c.Logo = logo
c.Name = name
cacheObjCastle.Store(logo, *c)
cacheObjCastle.Store(name, *c)
}
return nil
}
func addObjGuild(tag string, name string) (int64, error) {
tx, err := db.Begin()
logOnError(err, "addObjGuild : start transaction")
if err != nil {
return 0, err
}
res, err := tx.Exec(`INSERT INTO obj (obj_type_id, obj_sub_type_id)
VALUES (` + strconv.Itoa(objTypeGuild) + `,` + strconv.Itoa(objSubTypeGuild) + `);`)
logOnError(err, "addObjGuild : exec insert obj")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjGuild : rollback insert obj")
return 0, err
}
objId, err := res.LastInsertId()
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjGuild : rollback get lastInsertId")
return 0, err
}
stmt, err := tx.Prepare(`INSERT INTO obj_guild (obj_id, tag, name, chat_id, deposit_chat_id)
VALUES (?, ?, ?, NULL, NULL);`)
logOnError(err, "addObjGuild : prepare insert obj_guild")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjGuild : rollback prepare insert obj_guild")
return 0, err
}
defer stmt.Close()
_, err = stmt.Exec(objId, tag, name)
logOnError(err, "addObjGuild : exec insert obj_guild")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjGuild : rollback exec insert obj_guild")
return 0, err
}
err = tx.Commit()
logOnError(err, "addObjGuild : commit")
if err != nil {
return 0, err
}
return objId, nil
}
func getObjGuildID(s string) int64 {
if v, ok := cacheObjGuild.Load(s); ok {
g := v.(ChatWarsGuild)
return g.ObjID64
} else {
objID64, err := addObjGuild(s, ``)
logOnError(err, "getObjGuildID")
g := new(ChatWarsGuild)
g.ObjID64 = objID64
g.Tag = s
g.Name = ``
cacheObjGuild.Store(s, *g)
return objID64
}
}
func loadObjGuild() error {
var (
id int64
tag string
name string
)
cacheObjGuild = new(sync.Map)
guilds, err := db.Query(`SELECT og.obj_id, og.tag, og.name FROM obj_guild og;`)
if err != nil {
return err
}
defer guilds.Close()
for guilds.Next() {
err = guilds.Scan(&id, &tag, &name)
if err != nil {
return err
}
g := new(ChatWarsGuild)
g.ObjID64 = id
g.Tag = tag
g.Name = name
cacheObjGuild.Store(tag, *g)
}
return nil
}
func addObjUser(name string) (int64, error) {
tx, err := db.Begin()
logOnError(err, "addObjUser : start transaction")
if err != nil {
return 0, err
}
res, err := tx.Exec(`INSERT INTO obj (obj_type_id, obj_sub_type_id)
VALUES (` + strconv.Itoa(objTypeUser) + `,` + strconv.Itoa(objSubTypeUser) + `);`)
logOnError(err, "addObjUser : exec insert obj")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjUser : rollback insert obj")
return 0, err
}
objId, err := res.LastInsertId()
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjUser : rollback get lastInsertId")
return 0, err
}
stmt, err := tx.Prepare(`INSERT INTO obj_user (obj_id, name)
VALUES (?, ?);`)
logOnError(err, "addObjUser : prepare insert obj_user")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjUser : rollback prepare insert obj_user")
return 0, err
}
defer stmt.Close()
_, err = stmt.Exec(objId, name)
logOnError(err, "addObjUser : exec insert obj_user")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjUser : rollback exec insert obj_user")
return 0, err
}
err = tx.Commit()
logOnError(err, "addObjUser : commit")
if err != nil {
return 0, err
}
return objId, nil
}
func getObjUserID(s string) int64 {
if v, ok := cacheObjUser.Load(s); ok {
u := v.(ChatWarsUser)
return u.ObjID64
} else {
objID64, err := addObjUser(s)
logOnError(err, "getObjUserID")
u := new(ChatWarsUser)
u.ObjID64 = objID64
u.Name = s
cacheObjUser.Store(s, *u)
return objID64
}
}
func loadObjUser() error {
var (
id int64
name string
)
cacheObjUser = new(sync.Map)
users, err := db.Query(`SELECT ou.obj_id, ou.name FROM obj_user ou;`)
if err != nil {
return err
}
defer users.Close()
for users.Next() {
err = users.Scan(&id, &name)
if err != nil {
return err
}
u := new(ChatWarsUser)
u.ObjID64 = id
u.Name = name
cacheObjUser.Store(name, *u)
}
return nil
}
func getObjMsgDate(objID64 int64) (time.Time, error) {
m, err := getObjMsg(objID64)
if err != nil {
return time.Now(), err
} else {
return m.Date, nil
}
}
func addObjXP(userID64 int64, expNow int64, expLvl int64, level int64, date time.Time) (int64, error) {
tx, err := db.Begin()
logOnError(err, "addObjXP : start transaction")
if err != nil {
return 0, err
}
res, err := tx.Exec(`INSERT INTO obj (obj_type_id, obj_sub_type_id)
VALUES (` + strconv.Itoa(objTypeExperience) + `,` + strconv.Itoa(objSubTypeExperience) + `);`)
logOnError(err, "addObjXP : exec insert obj")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjXP : rollback insert obj")
return 0, err
}
objId, err := res.LastInsertId()
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjXP : rollback get lastInsertId")
return 0, err
}
stmt, err := tx.Prepare(`INSERT INTO obj_xp (obj_id, user_id, val, target, level, date)
VALUES (?, ?, ?, ?, ?, ?);`)
logOnError(err, "addObjXP : prepare insert obj_xp")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjXP : rollback prepare insert obj_xp")
return 0, err
}
defer stmt.Close()
_, err = stmt.Exec(objId, userID64, expNow, expLvl, level, date)
logOnError(err, "addObjXP : exec insert obj_xp")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjXP : rollback exec insert obj_xp")
return 0, err
}
err = tx.Commit()
logOnError(err, "addObjXP : commit")
if err != nil {
return 0, err
}
return objId, nil
}
func addObjItem(code string, name string, itemTypeID64 int64, weight int) (int64, error) {
if v, ok := cacheObjItem.Load(code); ok {
item := v.(ChatWarsItem)
if v, ok = cacheObjItem.Load(name); ok {
// obj is already added ?
return item.ObjID64, nil
} else {
err := objAddName(item.ObjID64, name)
if err != nil {
return 0, nil
} else {
return item.ObjID64, nil
}
}
}
tx, err := db.Begin()
logOnError(err, "addObjItem : start transaction")
if err != nil {
return 0, err
}
res, err := tx.Exec(`INSERT INTO obj (obj_type_id, obj_sub_type_id)
VALUES (` + strconv.Itoa(objTypeItem) + `,` + fmt.Sprintf("%d", itemTypeID64) + `);`)
logOnError(err, "addObjItem : exec insert obj")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjItem : rollback insert obj")
return 0, err
}
objId, err := res.LastInsertId()
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjItem : rollback get lastInsertId")
return 0, err
}
stmt, err := tx.Prepare(`INSERT INTO obj_item (obj_id, intl_id, weight)
VALUES (?, ?, ?);`)
logOnError(err, "addObjItem : prepare insert obj_item")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjItem : rollback prepare insert obj_item")
return 0, err
}
defer stmt.Close()
_, err = stmt.Exec(objId, code, weight)
logOnError(err, "addObjItem : exec insert obj_item")
if err != nil {
err2 := tx.Rollback()
logOnError(err2, "addObjItem : rollback exec insert obj_item")
return 0, err
}
err = tx.Commit()
logOnError(err, "addObjItem : commit")
if err != nil {
return 0, err
}
err = objAddName(objId, name)
logOnError(err, "addObjItem : add name")
c := new(ChatWarsItem)
c.ObjID64 = objId
c.Code = code
c.Name = name
c.Weight = weight
cacheObjItem.Store(code, *c)
cacheObjItem.Store(name, *c)
return objId, nil
}
func getObjItemID(c string, n string) int64 {
i := silentGetObjItemID(c, n)
if i == 0 {
/*
w := TGCommand{
Type: commandSendMsg,
Text: fmt.Sprintf("Object unknown : %s - %s\n", c, n),
ToUserID64: cfg.Bot.Admin,
}
TGCmdQueue <- w
*/
}
return i
}
func silentGetObjItemID(code string, name string) int64 {
if len(code) > 0 {
if v, ok := cacheObjItem.Load(code); ok {
item := v.(ChatWarsItem)
// log.Printf("Matching item code %s with %s.\n", code, item.Code)
return item.ObjID64
}
if ok, _ := regexp.MatchString(`^u[0-9]+`, code); !ok {
return 0
}
}
if len(name) == 0 {
return 0
}
if v, ok := cacheObjItem.Load(name); ok {
item := v.(ChatWarsItem)
//log.Printf("Matching item name %s with %s.\n", name, item.Name)
return item.ObjID64
}
if ok, _ := regexp.MatchString(`^((u|e)[0-9]+|(a|w)[0-9]+[a-e]{0,1})$`, code); ok || len(code) == 0 {
r := regexp.MustCompile(`^((?P<Modifier>⚡\+[0-9]+) ){0,1}(?P<BaseName>.+?)( \+(?P<Atk>[0-9]+)⚔){0,1}( \+(?P<Def>[0-9]+)🛡){0,1}( \+(?P<Mana>[0-9]+)💧){0,1}$`)
basename := r.ReplaceAllString(name, "${BaseName}")
if v, ok := cacheObjItem.Load(basename); ok && len(basename) > 0 {
item := v.(ChatWarsItem)
// log.Printf("Matching item full basename %s with %s.\n", basename, item.Name)
return item.ObjID64
}
item := ChatWarsItem{
ObjID64: 0,
}
cacheObjItem.Range(func(k, v interface{}) bool {
item = v.(ChatWarsItem)
if ok, _ := regexp.MatchString(`^(a|e|w)[0-9]+$`, item.Code); !ok { //only gear can be custom named
return true
}
m := fmt.Sprintf("^((%s.*)|(.*%s))$", regexp.QuoteMeta(item.Name), regexp.QuoteMeta(item.Name))
if ok, _ := regexp.MatchString(m, basename); ok {
// log.Printf("Matching item modified basename %s with %s. (1)\n", basename, item.Name)
return false
} else {
item = ChatWarsItem{
ObjID64: 0,
}
// log.Printf("Not matching item modified basename %s with %s.\n", basename, item.Name)
return true
}
})
if item.ObjID64 != 0 {
// log.Printf("Matching item modified basename %s with %s. (2)\n", basename, item.Name)
return item.ObjID64
} else {
fmt.Printf("silentGetObjItemID : Modifier : `%s`\n", r.ReplaceAllString(name, "${Modifier}"))
fmt.Printf("silentGetObjItemID : BaseName : `%s`\n", r.ReplaceAllString(name, "${BaseName}"))
fmt.Printf("silentGetObjItemID : Atk : `%s`\n", r.ReplaceAllString(name, "${Atk}"))
fmt.Printf("silentGetObjItemID : Def : `%s`\n", r.ReplaceAllString(name, "${Def}"))
fmt.Printf("silentGetObjItemID : Mana : `%s`\n", r.ReplaceAllString(name, "${Mana}"))
}
}
return 0
}
func loadObjItem() error {
var (
id int64
intl_id string
name string
weight int
)
cacheObjItem = new(sync.Map)
items, err := db.Query(`SELECT oi.obj_id, oi.intl_id, obn.name, oi.weight FROM obj_item oi, obj_name obn WHERE obn.obj_id = oi.obj_id AND obn.priority = 0;`)
if err != nil {
return err
}
defer items.Close()
for items.Next() {
err = items.Scan(&id, &intl_id, &name, &weight)
if err != nil {
return err
}
c := new(ChatWarsItem)
c.ObjID64 = id
c.Code = intl_id
c.Name = name
c.Weight = weight
cacheObjItem.Store(intl_id, *c)
cacheObjItem.Store(name, *c)
}
items2, err := db.Query(`SELECT oi.obj_id, oi.intl_id, obn.name, oi.weight FROM obj_item oi, obj_name obn WHERE obn.obj_id = oi.obj_id AND obn.priority > 0;`)
if err != nil {
return err
}
defer items2.Close()
for items2.Next() {
err = items2.Scan(&id, &intl_id, &name, &weight)
if err != nil {
return err
}
if _, ok := cacheObjItem.Load(intl_id); !ok {
c := new(ChatWarsItem)
c.ObjID64 = id
c.Code = intl_id
c.Name = name
c.Weight = weight
cacheObjItem.Store(name, *c)
} else {
log.Printf("loadObjItem : orphaned obj_name for `%s` : %s.\n", intl_id, name)
}
}
return nil
}