mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-27 18:12:50 +01:00
refactor(channelserver): extract Goocoo, Diva, Misc, Scenario, and Mercenary repositories
Move remaining raw s.server.db.* queries from handler files into dedicated repository structs, completing the repository extraction effort. Also adds SaveCharacterData and SaveHouseData to CharacterRepository. Fixes guild_hunts query to select both cats_used and start columns to match the existing two-column Scan call. Adds slot index validation in GoocooRepository to prevent SQL injection via fmt.Sprintf.
This commit is contained in:
@@ -75,14 +75,11 @@ func (save *CharacterSaveData) Save(s *Session) {
|
|||||||
save.compSave = save.decompSave
|
save.compSave = save.decompSave
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hr=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7
|
if err := s.server.charRepo.SaveCharacterData(save.CharID, save.compSave, save.HR, save.GR, save.Gender, save.WeaponType, save.WeaponID); err != nil {
|
||||||
`, save.compSave, save.HR, save.GR, save.Gender, save.WeaponType, save.WeaponID, save.CharID)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID))
|
s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := s.server.db.Exec(`UPDATE user_binary SET house_tier=$1, house_data=$2, bookshelf=$3, gallery=$4, tore=$5, garden=$6 WHERE id=$7
|
if err := s.server.charRepo.SaveHouseData(s.charID, save.HouseTier, save.HouseData, save.BookshelfData, save.GalleryData, save.ToreData, save.GardenData); err != nil {
|
||||||
`, save.HouseTier, save.HouseData, save.BookshelfData, save.GalleryData, save.ToreData, save.GardenData, s.charID); err != nil {
|
|
||||||
s.logger.Error("Failed to update user binary house data", zap.Error(err))
|
s.logger.Error("Failed to update user binary house data", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func cleanupDiva(s *Session) {
|
func cleanupDiva(s *Session) {
|
||||||
if _, err := s.server.db.Exec("DELETE FROM events WHERE event_type='diva'"); err != nil {
|
if err := s.server.divaRepo.DeleteEvents(); err != nil {
|
||||||
s.logger.Error("Failed to delete diva events", zap.Error(err))
|
s.logger.Error("Failed to delete diva events", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ func generateDivaTimestamps(s *Session, start uint32, debug bool) []uint32 {
|
|||||||
cleanupDiva(s)
|
cleanupDiva(s)
|
||||||
// Generate a new diva defense, starting midnight tomorrow
|
// Generate a new diva defense, starting midnight tomorrow
|
||||||
start = uint32(midnight.Add(24 * time.Hour).Unix())
|
start = uint32(midnight.Add(24 * time.Hour).Unix())
|
||||||
if _, err := s.server.db.Exec("INSERT INTO events (event_type, start_time) VALUES ('diva', to_timestamp($1)::timestamp without time zone)", start); err != nil {
|
if err := s.server.divaRepo.InsertEvent(start); err != nil {
|
||||||
s.logger.Error("Failed to insert diva event", zap.Error(err))
|
s.logger.Error("Failed to insert diva event", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
const divaIDSentinel = uint32(0xCAFEBEEF)
|
const divaIDSentinel = uint32(0xCAFEBEEF)
|
||||||
id, start := divaIDSentinel, uint32(0)
|
id, start := divaIDSentinel, uint32(0)
|
||||||
rows, err := s.server.db.Queryx("SELECT id, (EXTRACT(epoch FROM start_time)::int) as start_time FROM events WHERE event_type='diva'")
|
rows, err := s.server.divaRepo.GetEvents()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to query diva schedule", zap.Error(err))
|
s.logger.Error("Failed to query diva schedule", zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -9,17 +9,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func getGoocooData(s *Session, cid uint32) [][]byte {
|
func getGoocooData(s *Session, cid uint32) [][]byte {
|
||||||
var goocoo []byte
|
|
||||||
var goocoos [][]byte
|
var goocoos [][]byte
|
||||||
for i := 0; i < 5; i++ {
|
for i := uint32(0); i < 5; i++ {
|
||||||
err := s.server.db.QueryRow(fmt.Sprintf("SELECT goocoo%d FROM goocoo WHERE id=$1", i), cid).Scan(&goocoo)
|
goocoo, err := s.server.goocooRepo.GetSlot(cid, i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, err := s.server.db.Exec("INSERT INTO goocoo (id) VALUES ($1)", s.charID); err != nil {
|
if err := s.server.goocooRepo.EnsureExists(s.charID); err != nil {
|
||||||
s.logger.Error("Failed to insert goocoo record", zap.Error(err))
|
s.logger.Error("Failed to insert goocoo record", zap.Error(err))
|
||||||
}
|
}
|
||||||
return goocoos
|
return goocoos
|
||||||
}
|
}
|
||||||
if err == nil && goocoo != nil {
|
if goocoo != nil {
|
||||||
goocoos = append(goocoos, goocoo)
|
goocoos = append(goocoos, goocoo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,7 +44,7 @@ func handleMsgMhfUpdateGuacot(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if goocoo.Data1[0] == 0 {
|
if goocoo.Data1[0] == 0 {
|
||||||
if _, err := s.server.db.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=NULL WHERE id=$1", goocoo.Index), s.charID); err != nil {
|
if err := s.server.goocooRepo.ClearSlot(s.charID, goocoo.Index); err != nil {
|
||||||
s.logger.Error("Failed to clear goocoo slot", zap.Error(err))
|
s.logger.Error("Failed to clear goocoo slot", zap.Error(err))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -59,7 +58,7 @@ func handleMsgMhfUpdateGuacot(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
bf.WriteUint8(uint8(len(goocoo.Name)))
|
bf.WriteUint8(uint8(len(goocoo.Name)))
|
||||||
bf.WriteBytes(goocoo.Name)
|
bf.WriteBytes(goocoo.Name)
|
||||||
if _, err := s.server.db.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=$1 WHERE id=$2", goocoo.Index), bf.Data(), s.charID); err != nil {
|
if err := s.server.goocooRepo.SaveSlot(s.charID, goocoo.Index, bf.Data()); err != nil {
|
||||||
s.logger.Error("Failed to update goocoo slot", zap.Error(err))
|
s.logger.Error("Failed to update goocoo slot", zap.Error(err))
|
||||||
}
|
}
|
||||||
dumpSaveData(s, bf.Data(), fmt.Sprintf("goocoo-%d", goocoo.Index))
|
dumpSaveData(s, bf.Data(), fmt.Sprintf("goocoo-%d", goocoo.Index))
|
||||||
|
|||||||
@@ -168,8 +168,8 @@ func handleMsgMhfEnumerateMercenaryLog(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfCreateMercenary)
|
pkt := p.(*mhfpacket.MsgMhfCreateMercenary)
|
||||||
var nextID uint32
|
nextID, err := s.server.mercenaryRepo.NextRastaID()
|
||||||
if err := s.server.db.QueryRow("SELECT nextval('rasta_id_seq')").Scan(&nextID); err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get next rasta ID", zap.Error(err))
|
s.logger.Error("Failed to get next rasta ID", zap.Error(err))
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
doAckSimpleFail(s, pkt.AckHandle, nil)
|
||||||
return
|
return
|
||||||
@@ -227,7 +227,7 @@ func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
if pkt.Op != 2 && pkt.Op != 5 {
|
if pkt.Op != 2 && pkt.Op != 5 {
|
||||||
var loans uint8
|
var loans uint8
|
||||||
temp := byteframe.NewByteFrame()
|
temp := byteframe.NewByteFrame()
|
||||||
rows, err := s.server.db.Query("SELECT name, id, pact_id FROM characters WHERE pact_id=(SELECT rasta_id FROM characters WHERE id=$1)", s.charID)
|
rows, err := s.server.mercenaryRepo.GetMercenaryLoans(s.charID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to query mercenary loans", zap.Error(err))
|
s.logger.Error("Failed to query mercenary loans", zap.Error(err))
|
||||||
} else {
|
} else {
|
||||||
@@ -323,7 +323,8 @@ func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
dataLen := bf.ReadUint32()
|
dataLen := bf.ReadUint32()
|
||||||
catID := bf.ReadUint32()
|
catID := bf.ReadUint32()
|
||||||
if catID == 0 {
|
if catID == 0 {
|
||||||
if err := s.server.db.QueryRow("SELECT nextval('airou_id_seq')").Scan(&catID); err != nil {
|
catID, err = s.server.mercenaryRepo.NextAirouID()
|
||||||
|
if err != nil {
|
||||||
s.logger.Error("Failed to get next airou ID", zap.Error(err))
|
s.logger.Error("Failed to get next airou ID", zap.Error(err))
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
return
|
return
|
||||||
@@ -392,9 +393,7 @@ func getGuildAirouList(s *Session) []Airou {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return guildCats
|
return guildCats
|
||||||
}
|
}
|
||||||
rows, err := s.server.db.Query(`SELECT cats_used FROM guild_hunts gh
|
rows, err := s.server.mercenaryRepo.GetGuildHuntCatsUsed(s.charID)
|
||||||
INNER JOIN characters c ON gh.host_id = c.id WHERE c.id=$1
|
|
||||||
`, s.charID)
|
|
||||||
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
|
return guildCats
|
||||||
@@ -414,10 +413,7 @@ func getGuildAirouList(s *Session) []Airou {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err = s.server.db.Query(`SELECT c.otomoairou FROM characters c
|
rows, err = s.server.mercenaryRepo.GetGuildAirou(guild.ID)
|
||||||
INNER JOIN guild_characters gc ON gc.character_id = c.id
|
|
||||||
WHERE gc.guild_id = $1 AND c.otomoairou IS NOT NULL
|
|
||||||
ORDER BY c.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
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ func handleMsgMhfGetTrendWeapon(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
pkt := p.(*mhfpacket.MsgMhfGetTrendWeapon)
|
pkt := p.(*mhfpacket.MsgMhfGetTrendWeapon)
|
||||||
trendWeapons := [14][3]TrendWeapon{}
|
trendWeapons := [14][3]TrendWeapon{}
|
||||||
for i := uint8(0); i < 14; i++ {
|
for i := uint8(0); i < 14; i++ {
|
||||||
rows, err := s.server.db.Query(`SELECT weapon_id FROM trend_weapons WHERE weapon_type=$1 ORDER BY count DESC LIMIT 3`, i)
|
rows, err := s.server.miscRepo.GetTrendWeapons(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -288,8 +288,7 @@ func handleMsgMhfGetTrendWeapon(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfUpdateUseTrendWeaponLog(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfUpdateUseTrendWeaponLog(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfUpdateUseTrendWeaponLog)
|
pkt := p.(*mhfpacket.MsgMhfUpdateUseTrendWeaponLog)
|
||||||
if _, err := s.server.db.Exec(`INSERT INTO trend_weapons (weapon_id, weapon_type, count) VALUES ($1, $2, 1) ON CONFLICT (weapon_id) DO
|
if err := s.server.miscRepo.UpsertTrendWeapon(pkt.WeaponID, pkt.WeaponType); err != nil {
|
||||||
UPDATE SET count = trend_weapons.count+1`, pkt.WeaponID, pkt.WeaponType); err != nil {
|
|
||||||
s.logger.Error("Failed to update trend weapon log", zap.Error(err))
|
s.logger.Error("Failed to update trend weapon log", zap.Error(err))
|
||||||
}
|
}
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func handleMsgMhfInfoScenarioCounter(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
pkt := p.(*mhfpacket.MsgMhfInfoScenarioCounter)
|
pkt := p.(*mhfpacket.MsgMhfInfoScenarioCounter)
|
||||||
var scenarios []Scenario
|
var scenarios []Scenario
|
||||||
var scenario Scenario
|
var scenario Scenario
|
||||||
scenarioData, err := s.server.db.Queryx("SELECT scenario_id, category_id FROM scenario_counter")
|
scenarioData, err := s.server.scenarioRepo.GetCounters()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = scenarioData.Close()
|
_ = scenarioData.Close()
|
||||||
s.logger.Error("Failed to get scenario counter info from db", zap.Error(err))
|
s.logger.Error("Failed to get scenario counter info from db", zap.Error(err))
|
||||||
|
|||||||
@@ -210,3 +210,17 @@ func (r *CharacterRepository) FindByRastaID(rastaID int) (charID uint32, name st
|
|||||||
err = r.db.QueryRow("SELECT name, id FROM characters WHERE rasta_id=$1", rastaID).Scan(&name, &charID)
|
err = r.db.QueryRow("SELECT name, id FROM characters WHERE rasta_id=$1", rastaID).Scan(&name, &charID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveCharacterData updates the core save fields on a character.
|
||||||
|
func (r *CharacterRepository) SaveCharacterData(charID uint32, compSave []byte, hr, gr uint16, isFemale bool, weaponType uint8, weaponID uint16) error {
|
||||||
|
_, err := r.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hr=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7`,
|
||||||
|
compSave, hr, gr, isFemale, weaponType, weaponID, charID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveHouseData updates house-related fields in user_binary.
|
||||||
|
func (r *CharacterRepository) SaveHouseData(charID uint32, houseTier []byte, houseData, bookshelf, gallery, tore, garden []byte) error {
|
||||||
|
_, err := r.db.Exec(`UPDATE user_binary SET house_tier=$1, house_data=$2, bookshelf=$3, gallery=$4, tore=$5, garden=$6 WHERE id=$7`,
|
||||||
|
houseTier, houseData, bookshelf, gallery, tore, garden, charID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
32
server/channelserver/repo_diva.go
Normal file
32
server/channelserver/repo_diva.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DivaRepository centralizes all database access for diva defense events.
|
||||||
|
type DivaRepository struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDivaRepository creates a new DivaRepository.
|
||||||
|
func NewDivaRepository(db *sqlx.DB) *DivaRepository {
|
||||||
|
return &DivaRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteEvents removes all diva events.
|
||||||
|
func (r *DivaRepository) DeleteEvents() error {
|
||||||
|
_, err := r.db.Exec("DELETE FROM events WHERE event_type='diva'")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// InsertEvent creates a new diva event with the given start epoch.
|
||||||
|
func (r *DivaRepository) InsertEvent(startEpoch uint32) error {
|
||||||
|
_, err := r.db.Exec("INSERT INTO events (event_type, start_time) VALUES ('diva', to_timestamp($1)::timestamp without time zone)", startEpoch)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEvents returns all diva events with their ID and start_time epoch.
|
||||||
|
func (r *DivaRepository) GetEvents() (*sqlx.Rows, error) {
|
||||||
|
return r.db.Queryx("SELECT id, (EXTRACT(epoch FROM start_time)::int) as start_time FROM events WHERE event_type='diva'")
|
||||||
|
}
|
||||||
59
server/channelserver/repo_goocoo.go
Normal file
59
server/channelserver/repo_goocoo.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GoocooRepository centralizes all database access for the goocoo table.
|
||||||
|
type GoocooRepository struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGoocooRepository creates a new GoocooRepository.
|
||||||
|
func NewGoocooRepository(db *sqlx.DB) *GoocooRepository {
|
||||||
|
return &GoocooRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// validGoocooSlot validates the slot index to prevent SQL injection.
|
||||||
|
func validGoocooSlot(slot uint32) error {
|
||||||
|
if slot > 4 {
|
||||||
|
return fmt.Errorf("invalid goocoo slot index: %d", slot)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnsureExists creates a goocoo record if it doesn't already exist.
|
||||||
|
func (r *GoocooRepository) EnsureExists(charID uint32) error {
|
||||||
|
_, err := r.db.Exec("INSERT INTO goocoo (id) VALUES ($1) ON CONFLICT DO NOTHING", charID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSlot reads a single goocoo slot by character ID and slot index (0-4).
|
||||||
|
func (r *GoocooRepository) GetSlot(charID uint32, slot uint32) ([]byte, error) {
|
||||||
|
if err := validGoocooSlot(slot); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var data []byte
|
||||||
|
err := r.db.QueryRow(fmt.Sprintf("SELECT goocoo%d FROM goocoo WHERE id=$1", slot), charID).Scan(&data)
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearSlot sets a goocoo slot to NULL.
|
||||||
|
func (r *GoocooRepository) ClearSlot(charID uint32, slot uint32) error {
|
||||||
|
if err := validGoocooSlot(slot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := r.db.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=NULL WHERE id=$1", slot), charID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveSlot writes data to a goocoo slot.
|
||||||
|
func (r *GoocooRepository) SaveSlot(charID uint32, slot uint32, data []byte) error {
|
||||||
|
if err := validGoocooSlot(slot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := r.db.Exec(fmt.Sprintf("UPDATE goocoo SET goocoo%d=$1 WHERE id=$2", slot), data, charID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
50
server/channelserver/repo_mercenary.go
Normal file
50
server/channelserver/repo_mercenary.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MercenaryRepository centralizes database access for mercenary/rasta/airou sequences and queries.
|
||||||
|
type MercenaryRepository struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMercenaryRepository creates a new MercenaryRepository.
|
||||||
|
func NewMercenaryRepository(db *sqlx.DB) *MercenaryRepository {
|
||||||
|
return &MercenaryRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextRastaID returns the next value from the rasta_id_seq sequence.
|
||||||
|
func (r *MercenaryRepository) NextRastaID() (uint32, error) {
|
||||||
|
var id uint32
|
||||||
|
err := r.db.QueryRow("SELECT nextval('rasta_id_seq')").Scan(&id)
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextAirouID returns the next value from the airou_id_seq sequence.
|
||||||
|
func (r *MercenaryRepository) NextAirouID() (uint32, error) {
|
||||||
|
var id uint32
|
||||||
|
err := r.db.QueryRow("SELECT nextval('airou_id_seq')").Scan(&id)
|
||||||
|
return id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMercenaryLoans returns characters that have a pact with the given character's rasta_id.
|
||||||
|
func (r *MercenaryRepository) GetMercenaryLoans(charID uint32) (*sql.Rows, error) {
|
||||||
|
return r.db.Query("SELECT name, id, pact_id FROM characters WHERE pact_id=(SELECT rasta_id FROM characters WHERE id=$1)", charID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGuildHuntCatsUsed returns cats_used and start from guild_hunts for a given character.
|
||||||
|
func (r *MercenaryRepository) GetGuildHuntCatsUsed(charID uint32) (*sql.Rows, error) {
|
||||||
|
return r.db.Query(`SELECT cats_used, start FROM guild_hunts gh
|
||||||
|
INNER JOIN characters c ON gh.host_id = c.id WHERE c.id=$1`, charID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGuildAirou returns otomoairou data for all characters in a guild.
|
||||||
|
func (r *MercenaryRepository) GetGuildAirou(guildID uint32) (*sql.Rows, error) {
|
||||||
|
return r.db.Query(`SELECT c.otomoairou FROM characters c
|
||||||
|
INNER JOIN guild_characters gc ON gc.character_id = c.id
|
||||||
|
WHERE gc.guild_id = $1 AND c.otomoairou IS NOT NULL
|
||||||
|
ORDER BY c.id LIMIT 60`, guildID)
|
||||||
|
}
|
||||||
29
server/channelserver/repo_misc.go
Normal file
29
server/channelserver/repo_misc.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MiscRepository centralizes database access for miscellaneous game tables.
|
||||||
|
type MiscRepository struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMiscRepository creates a new MiscRepository.
|
||||||
|
func NewMiscRepository(db *sqlx.DB) *MiscRepository {
|
||||||
|
return &MiscRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTrendWeapons returns the top 3 weapon IDs for a given weapon type, ordered by count descending.
|
||||||
|
func (r *MiscRepository) GetTrendWeapons(weaponType uint8) (*sql.Rows, error) {
|
||||||
|
return r.db.Query("SELECT weapon_id FROM trend_weapons WHERE weapon_type=$1 ORDER BY count DESC LIMIT 3", weaponType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpsertTrendWeapon increments the count for a weapon, inserting it if it doesn't exist.
|
||||||
|
func (r *MiscRepository) UpsertTrendWeapon(weaponID uint16, weaponType uint8) error {
|
||||||
|
_, err := r.db.Exec(`INSERT INTO trend_weapons (weapon_id, weapon_type, count) VALUES ($1, $2, 1) ON CONFLICT (weapon_id) DO
|
||||||
|
UPDATE SET count = trend_weapons.count+1`, weaponID, weaponType)
|
||||||
|
return err
|
||||||
|
}
|
||||||
20
server/channelserver/repo_scenario.go
Normal file
20
server/channelserver/repo_scenario.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ScenarioRepository centralizes all database access for the scenario_counter table.
|
||||||
|
type ScenarioRepository struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewScenarioRepository creates a new ScenarioRepository.
|
||||||
|
func NewScenarioRepository(db *sqlx.DB) *ScenarioRepository {
|
||||||
|
return &ScenarioRepository{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCounters returns all scenario counters.
|
||||||
|
func (r *ScenarioRepository) GetCounters() (*sqlx.Rows, error) {
|
||||||
|
return r.db.Queryx("SELECT scenario_id, category_id FROM scenario_counter")
|
||||||
|
}
|
||||||
@@ -62,6 +62,11 @@ type Server struct {
|
|||||||
achievementRepo *AchievementRepository
|
achievementRepo *AchievementRepository
|
||||||
shopRepo *ShopRepository
|
shopRepo *ShopRepository
|
||||||
cafeRepo *CafeRepository
|
cafeRepo *CafeRepository
|
||||||
|
goocooRepo *GoocooRepository
|
||||||
|
divaRepo *DivaRepository
|
||||||
|
miscRepo *MiscRepository
|
||||||
|
scenarioRepo *ScenarioRepository
|
||||||
|
mercenaryRepo *MercenaryRepository
|
||||||
erupeConfig *cfg.Config
|
erupeConfig *cfg.Config
|
||||||
acceptConns chan net.Conn
|
acceptConns chan net.Conn
|
||||||
deleteConns chan net.Conn
|
deleteConns chan net.Conn
|
||||||
@@ -148,6 +153,11 @@ func NewServer(config *Config) *Server {
|
|||||||
s.achievementRepo = NewAchievementRepository(config.DB)
|
s.achievementRepo = NewAchievementRepository(config.DB)
|
||||||
s.shopRepo = NewShopRepository(config.DB)
|
s.shopRepo = NewShopRepository(config.DB)
|
||||||
s.cafeRepo = NewCafeRepository(config.DB)
|
s.cafeRepo = NewCafeRepository(config.DB)
|
||||||
|
s.goocooRepo = NewGoocooRepository(config.DB)
|
||||||
|
s.divaRepo = NewDivaRepository(config.DB)
|
||||||
|
s.miscRepo = NewMiscRepository(config.DB)
|
||||||
|
s.scenarioRepo = NewScenarioRepository(config.DB)
|
||||||
|
s.mercenaryRepo = NewMercenaryRepository(config.DB)
|
||||||
|
|
||||||
// Mezeporta
|
// Mezeporta
|
||||||
s.stages["sl1Ns200p0a0u0"] = NewStage("sl1Ns200p0a0u0")
|
s.stages["sl1Ns200p0a0u0"] = NewStage("sl1Ns200p0a0u0")
|
||||||
|
|||||||
Reference in New Issue
Block a user