Merge branch 'main' of github.com:ZeruLight/Erupe

This commit is contained in:
wish
2023-10-24 21:25:34 +11:00
8 changed files with 145 additions and 145 deletions

View File

@@ -40,6 +40,7 @@
"MaximumNP": 100000, "MaximumNP": 100000,
"MaximumRP": 50000, "MaximumRP": 50000,
"MaximumFP": 120000, "MaximumFP": 120000,
"TreasureHuntExpiry": 604800,
"DisableLoginBoost": false, "DisableLoginBoost": false,
"DisableBoostTime": false, "DisableBoostTime": false,
"BoostTimeDuration": 120, "BoostTimeDuration": 120,

View File

@@ -125,6 +125,8 @@ type GameplayOptions struct {
MaximumNP int // Maximum number of NP held by a player MaximumNP int // Maximum number of NP held by a player
MaximumRP uint16 // Maximum number of RP held by a player MaximumRP uint16 // Maximum number of RP held by a player
MaximumFP uint32 // Maximum number of FP held by a player MaximumFP uint32 // Maximum number of FP held by a player
TreasureHuntExpiry uint32 // Seconds until a Clan Treasure Hunt will expire
TreasureHuntPartnyaCooldown uint32 // Seconds until a Partnya can be assigned to another Clan Treasure Hunt
DisableLoginBoost bool // Disables the Login Boost system DisableLoginBoost bool // Disables the Login Boost system
DisableBoostTime bool // Disables the daily NetCafe Boost Time DisableBoostTime bool // Disables the daily NetCafe Boost Time
BoostTimeDuration int // The number of minutes NetCafe Boost Time lasts for BoostTimeDuration int // The number of minutes NetCafe Boost Time lasts for

View File

@@ -126,6 +126,7 @@ func main() {
// Clear stale data // Clear stale data
_ = db.MustExec("DELETE FROM sign_sessions") _ = db.MustExec("DELETE FROM sign_sessions")
_ = db.MustExec("DELETE FROM servers") _ = db.MustExec("DELETE FROM servers")
_ = db.MustExec(`UPDATE guild_characters SET treasure_hunt=NULL`)
// Clean the DB if the option is on. // Clean the DB if the option is on.
if config.DevMode && config.DevModeOptions.CleanDB { if config.DevMode && config.DevModeOptions.CleanDB {

View File

@@ -12,7 +12,8 @@ import (
type MsgMhfEnumerateGuildTresure struct { type MsgMhfEnumerateGuildTresure struct {
AckHandle uint32 AckHandle uint32
MaxHunts uint16 MaxHunts uint16
Unk uint32 Unk0 uint16
Unk1 uint16
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -24,9 +25,8 @@ func (m *MsgMhfEnumerateGuildTresure) Opcode() network.PacketID {
func (m *MsgMhfEnumerateGuildTresure) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfEnumerateGuildTresure) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.MaxHunts = bf.ReadUint16() m.MaxHunts = bf.ReadUint16()
// Changes with MaxHunts m.Unk0 = bf.ReadUint16()
// 0 if MaxHunts = 1, 1 if MaxHunts = 30 m.Unk1 = bf.ReadUint16()
m.Unk = bf.ReadUint32()
return nil return nil
} }

View File

@@ -0,0 +1,26 @@
BEGIN;
ALTER TABLE IF EXISTS public.guild_hunts DROP COLUMN IF EXISTS hunters;
ALTER TABLE IF EXISTS public.guild_characters
ADD COLUMN treasure_hunt integer;
ALTER TABLE IF EXISTS public.guild_hunts
ADD COLUMN start timestamp with time zone NOT NULL DEFAULT now();
UPDATE guild_hunts SET start=to_timestamp(return);
ALTER TABLE IF EXISTS public.guild_hunts DROP COLUMN IF EXISTS "return";
ALTER TABLE IF EXISTS public.guild_hunts
RENAME claimed TO collected;
CREATE TABLE public.guild_hunts_claimed
(
hunt_id integer NOT NULL,
character_id integer NOT NULL
);
ALTER TABLE IF EXISTS public.guild_hunts DROP COLUMN IF EXISTS treasure;
END;

View File

@@ -233,7 +233,7 @@ func logoutPlayer(s *Session) {
s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID) s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID)
treasureHuntUnregister(s) s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE character_id=$1`, s.charID)
if s.stage == nil { if s.stage == nil {
return return

View File

@@ -4,72 +4,79 @@ import (
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/common/stringsupport" "erupe-ce/common/stringsupport"
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
"time"
) )
type TreasureHunt struct { type TreasureHunt struct {
HuntID uint32 `db:"id"` HuntID uint32 `db:"id"`
HostID uint32 `db:"host_id"` HostID uint32 `db:"host_id"`
Destination uint32 `db:"destination"` Destination uint32 `db:"destination"`
Level uint32 `db:"level"` Level uint32 `db:"level"`
Return uint32 `db:"return"` Start time.Time `db:"start"`
Acquired bool `db:"acquired"` Acquired bool `db:"acquired"`
Claimed bool `db:"claimed"` Collected bool `db:"collected"`
Hunters string `db:"hunters"` HuntData []byte `db:"hunt_data"`
Treasure string `db:"treasure"` Hunters uint32 `db:"hunters"`
HuntData []byte `db:"hunt_data"` Claimed bool `db:"claimed"`
} }
func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateGuildTresure) pkt := p.(*mhfpacket.MsgMhfEnumerateGuildTresure)
guild, err := GetGuildInfoByCharacterId(s, s.charID) guild, err := GetGuildInfoByCharacterId(s, s.charID)
if err != nil { if err != nil || guild == nil {
panic(err) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
}
var hunts []TreasureHunt
var hunt TreasureHunt
switch pkt.MaxHunts {
case 1:
err = s.server.db.QueryRowx(`SELECT id, host_id, destination, level, start, hunt_data FROM guild_hunts WHERE host_id=$1 AND acquired=FALSE`, s.charID).StructScan(&hunt)
if err == nil {
hunts = append(hunts, hunt)
}
case 30:
rows, err := s.server.db.Queryx(`SELECT gh.id, gh.host_id, gh.destination, gh.level, gh.start, gh.collected, gh.hunt_data,
(SELECT COUNT(*) FROM guild_characters gc WHERE gc.treasure_hunt = gh.id AND gc.character_id <> $1) AS hunters,
CASE
WHEN ghc.character_id IS NOT NULL THEN true
ELSE false
END AS claimed
FROM guild_hunts gh
LEFT JOIN guild_hunts_claimed ghc ON gh.id = ghc.hunt_id AND ghc.character_id = $1
WHERE gh.guild_id=$2 AND gh.level=2 AND gh.acquired=TRUE
`, s.charID, guild.ID)
if err != nil {
rows.Close()
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
return
} else {
for rows.Next() {
err = rows.StructScan(&hunt)
if err == nil && hunt.Start.Add(time.Second*time.Duration(s.server.erupeConfig.GameplayOptions.TreasureHuntExpiry)).After(TimeAdjusted()) {
hunts = append(hunts, hunt)
}
}
}
if len(hunts) > 30 {
hunts = hunts[:30]
}
} }
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
hunts := 0 bf.WriteUint16(uint16(len(hunts)))
rows, _ := s.server.db.Queryx("SELECT id, host_id, destination, level, return, acquired, claimed, hunters, treasure, hunt_data FROM guild_hunts WHERE guild_id=$1 AND $2 < return+604800", guild.ID, TimeAdjusted().Unix()) bf.WriteUint16(uint16(len(hunts)))
for rows.Next() { for _, h := range hunts {
hunt := &TreasureHunt{} bf.WriteUint32(h.HuntID)
err = rows.StructScan(&hunt) bf.WriteUint32(h.Destination)
// Remove self from other hunter count bf.WriteUint32(h.Level)
hunt.Hunters = stringsupport.CSVRemove(hunt.Hunters, int(s.charID)) bf.WriteUint32(h.Hunters)
if err != nil { bf.WriteUint32(uint32(h.Start.Unix()))
panic(err) bf.WriteBool(h.Collected)
} bf.WriteBool(h.Claimed)
if pkt.MaxHunts == 1 { bf.WriteBytes(h.HuntData)
if hunt.HostID != s.charID || hunt.Acquired {
continue
}
hunts++
bf.WriteUint32(hunt.HuntID)
bf.WriteUint32(hunt.Destination)
bf.WriteUint32(hunt.Level)
bf.WriteUint32(uint32(stringsupport.CSVLength(hunt.Hunters)))
bf.WriteUint32(hunt.Return)
bf.WriteBool(false)
bf.WriteBool(false)
bf.WriteBytes(hunt.HuntData)
break
} else if pkt.MaxHunts == 30 && hunt.Acquired && hunt.Level == 2 {
if hunts == 30 {
break
}
hunts++
bf.WriteUint32(hunt.HuntID)
bf.WriteUint32(hunt.Destination)
bf.WriteUint32(hunt.Level)
bf.WriteUint32(uint32(stringsupport.CSVLength(hunt.Hunters)))
bf.WriteUint32(hunt.Return)
bf.WriteBool(hunt.Claimed)
bf.WriteBool(stringsupport.CSVContains(hunt.Treasure, int(s.charID)))
bf.WriteBytes(hunt.HuntData)
}
} }
resp := byteframe.NewByteFrame() doAckBufSucceed(s, pkt.AckHandle, bf.Data())
resp.WriteUint16(uint16(hunts))
resp.WriteUint16(uint16(hunts))
resp.WriteBytes(bf.Data())
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
} }
func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) {
@@ -77,8 +84,9 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrameFromBytes(pkt.Data) bf := byteframe.NewByteFrameFromBytes(pkt.Data)
huntData := byteframe.NewByteFrame() huntData := byteframe.NewByteFrame()
guild, err := GetGuildInfoByCharacterId(s, s.charID) guild, err := GetGuildInfoByCharacterId(s, s.charID)
if err != nil { if err != nil || guild == nil {
panic(err) doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
} }
guildCats := getGuildAirouList(s) guildCats := getGuildAirouList(s)
destination := bf.ReadUint32() destination := bf.ReadUint32()
@@ -100,79 +108,47 @@ func handleMsgMhfRegistGuildTresure(s *Session, p mhfpacket.MHFPacket) {
huntData.WriteBytes(bf.ReadBytes(9)) huntData.WriteBytes(bf.ReadBytes(9))
} }
} }
_, err = s.server.db.Exec("INSERT INTO guild_hunts (guild_id, host_id, destination, level, return, hunt_data, cats_used) VALUES ($1, $2, $3, $4, $5, $6, $7)", s.server.db.Exec(`INSERT INTO guild_hunts (guild_id, host_id, destination, level, hunt_data, cats_used) VALUES ($1, $2, $3, $4, $5, $6)
guild.ID, s.charID, destination, level, TimeAdjusted().Unix(), huntData.Data(), catsUsed) `, guild.ID, s.charID, destination, level, huntData.Data(), catsUsed)
if err != nil {
panic(err)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }
func handleMsgMhfAcquireGuildTresure(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireGuildTresure(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireGuildTresure) pkt := p.(*mhfpacket.MsgMhfAcquireGuildTresure)
_, err := s.server.db.Exec("UPDATE guild_hunts SET acquired=true WHERE id=$1", pkt.HuntID) s.server.db.Exec(`UPDATE guild_hunts SET acquired=true WHERE id=$1`, pkt.HuntID)
if err != nil {
panic(err)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }
func treasureHuntUnregister(s *Session) {
guild, err := GetGuildInfoByCharacterId(s, s.charID)
if err != nil || guild == nil {
return
}
var huntID int
var hunters string
rows, err := s.server.db.Queryx("SELECT id, hunters FROM guild_hunts WHERE guild_id=$1", guild.ID)
if err != nil {
return
}
for rows.Next() {
rows.Scan(&huntID, &hunters)
hunters = stringsupport.CSVRemove(hunters, int(s.charID))
s.server.db.Exec("UPDATE guild_hunts SET hunters=$1 WHERE id=$2", hunters, huntID)
}
}
func handleMsgMhfOperateGuildTresureReport(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfOperateGuildTresureReport(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfOperateGuildTresureReport) pkt := p.(*mhfpacket.MsgMhfOperateGuildTresureReport)
var csv string switch pkt.State {
if pkt.State == 0 { // Report registration case 0: // Report registration
// Unregister from all other hunts s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=$1 WHERE character_id=$2`, pkt.HuntID, s.charID)
treasureHuntUnregister(s) case 1: // Collected by hunter
if pkt.HuntID != 0 { s.server.db.Exec(`UPDATE guild_hunts SET collected=true WHERE id=$1`, pkt.HuntID)
// Register to selected hunt s.server.db.Exec(`UPDATE guild_characters SET treasure_hunt=NULL WHERE treasure_hunt=$1`, pkt.HuntID)
err := s.server.db.QueryRow("SELECT hunters FROM guild_hunts WHERE id=$1", pkt.HuntID).Scan(&csv) case 2: // Claim treasure
if err != nil { s.server.db.Exec(`INSERT INTO guild_hunts_claimed VALUES ($1, $2)`, pkt.HuntID, s.charID)
panic(err)
}
csv = stringsupport.CSVAdd(csv, int(s.charID))
_, err = s.server.db.Exec("UPDATE guild_hunts SET hunters=$1 WHERE id=$2", csv, pkt.HuntID)
if err != nil {
panic(err)
}
}
} else if pkt.State == 1 { // Collected by hunter
s.server.db.Exec("UPDATE guild_hunts SET hunters='', claimed=true WHERE id=$1", pkt.HuntID)
} else if pkt.State == 2 { // Claim treasure
err := s.server.db.QueryRow("SELECT treasure FROM guild_hunts WHERE id=$1", pkt.HuntID).Scan(&csv)
if err != nil {
panic(err)
}
csv = stringsupport.CSVAdd(csv, int(s.charID))
_, err = s.server.db.Exec("UPDATE guild_hunts SET treasure=$1 WHERE id=$2", csv, pkt.HuntID)
if err != nil {
panic(err)
}
} }
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }
type TreasureSouvenir struct {
Destination uint32
Quantity uint32
}
func handleMsgMhfGetGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGuildTresureSouvenir) pkt := p.(*mhfpacket.MsgMhfGetGuildTresureSouvenir)
bf := byteframe.NewByteFrame()
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 6)) bf.WriteUint32(0)
souvenirs := []TreasureSouvenir{}
bf.WriteUint16(uint16(len(souvenirs)))
for _, souvenir := range souvenirs {
bf.WriteUint32(souvenir.Destination)
bf.WriteUint32(souvenir.Quantity)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleMsgMhfAcquireGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfAcquireGuildTresureSouvenir(s *Session, p mhfpacket.MHFPacket) {

View File

@@ -325,44 +325,38 @@ type Airou struct {
} }
func getGuildAirouList(s *Session) []Airou { func getGuildAirouList(s *Session) []Airou {
var guild *Guild
var err error
var guildCats []Airou var guildCats []Airou
bannedCats := make(map[uint32]int)
// returning 0 cats on any guild issues guild, err := GetGuildInfoByCharacterId(s, s.charID)
// can probably optimise all of the guild queries pretty heavily
guild, err = GetGuildInfoByCharacterId(s, s.charID)
if err != nil { if err != nil {
return guildCats return guildCats
} }
rows, err := s.server.db.Query(`SELECT cats_used FROM guild_hunts gh
// Get cats used recently INNER JOIN characters c ON gh.host_id = c.id WHERE c.id=$1
// Retail reset at midday, 12 hours is a midpoint `, s.charID)
tempBanDuration := 43200 - (1800) // Minus hunt time
bannedCats := make(map[uint32]int)
var csvTemp string
rows, err := s.server.db.Query(`SELECT cats_used
FROM guild_hunts gh
INNER JOIN characters c
ON gh.host_id = c.id
WHERE c.id=$1 AND gh.return+$2>$3`, s.charID, tempBanDuration, TimeAdjusted().Unix())
if err != nil { if err != nil {
s.logger.Warn("Failed to get recently used airous", zap.Error(err)) s.logger.Warn("Failed to get recently used airous", zap.Error(err))
return guildCats
} }
var csvTemp string
var startTemp time.Time
for rows.Next() { for rows.Next() {
rows.Scan(&csvTemp) err = rows.Scan(&csvTemp, &startTemp)
for i, j := range stringsupport.CSVElems(csvTemp) { if err != nil {
bannedCats[uint32(j)] = i continue
}
if startTemp.Add(time.Second * time.Duration(s.server.erupeConfig.GameplayOptions.TreasureHuntPartnyaCooldown)).Before(TimeAdjusted()) {
for i, j := range stringsupport.CSVElems(csvTemp) {
bannedCats[uint32(j)] = i
}
} }
} }
rows, err = s.server.db.Query(`SELECT c.otomoairou rows, err = s.server.db.Query(`SELECT c.otomoairou FROM characters c
FROM characters c INNER JOIN guild_characters gc ON gc.character_id = c.id
INNER JOIN guild_characters gc
ON gc.character_id = c.id
WHERE gc.guild_id = $1 AND c.otomoairou IS NOT NULL WHERE gc.guild_id = $1 AND c.otomoairou IS NOT NULL
ORDER BY c.id ASC ORDER BY c.id LIMIT 60`, guild.ID)
LIMIT 60;`, guild.ID)
if err != nil { if err != nil {
s.logger.Warn("Selecting otomoairou based on guild failed", zap.Error(err)) s.logger.Warn("Selecting otomoairou based on guild failed", zap.Error(err))
return guildCats return guildCats