mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
Merge branch 'main' into feature/festa
This commit is contained in:
@@ -9,10 +9,6 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"io/ioutil"
|
||||
"math/bits"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
@@ -20,6 +16,9 @@ import (
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/text/encoding/japanese"
|
||||
"golang.org/x/text/transform"
|
||||
"io/ioutil"
|
||||
"math/bits"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// Temporary function to just return no results for a MSG_MHF_ENUMERATE* packet
|
||||
@@ -80,7 +79,7 @@ func doAckSimpleFail(s *Session, ackHandle uint32, data []byte) {
|
||||
func updateRights(s *Session) {
|
||||
update := &mhfpacket.MsgSysUpdateRight{
|
||||
ClientRespAckHandle: 0,
|
||||
Unk1: s.rights,
|
||||
Bitfield: s.rights,
|
||||
Rights: []mhfpacket.ClientRight{
|
||||
{
|
||||
ID: 1,
|
||||
@@ -192,8 +191,12 @@ func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
func logoutPlayer(s *Session) {
|
||||
delete(s.server.sessions, s.rawConn)
|
||||
s.rawConn.Close()
|
||||
if _, exists := s.server.sessions[s.rawConn]; exists {
|
||||
delete(s.server.sessions, s.rawConn)
|
||||
s.rawConn.Close()
|
||||
} else {
|
||||
return // Prevent re-running logout logic on real logouts
|
||||
}
|
||||
|
||||
_, err := s.server.db.Exec("UPDATE sign_sessions SET server_id=NULL, char_id=NULL WHERE token=$1", s.token)
|
||||
if err != nil {
|
||||
@@ -206,13 +209,13 @@ func logoutPlayer(s *Session) {
|
||||
}
|
||||
|
||||
var timePlayed int
|
||||
var sessionTime int
|
||||
_ = s.server.db.QueryRow("SELECT time_played FROM characters WHERE id = $1", s.charID).Scan(&timePlayed)
|
||||
|
||||
timePlayed = (int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)) + timePlayed
|
||||
sessionTime = int(Time_Current_Adjusted().Unix()) - int(s.sessionStart)
|
||||
timePlayed += sessionTime
|
||||
|
||||
var rpGained int
|
||||
|
||||
if s.rights > 0x40000000 { // N Course
|
||||
if s.rights >= 0x40000000 { // N Course
|
||||
rpGained = timePlayed / 900
|
||||
timePlayed = timePlayed % 900
|
||||
} else {
|
||||
@@ -220,10 +223,10 @@ func logoutPlayer(s *Session) {
|
||||
timePlayed = timePlayed % 1800
|
||||
}
|
||||
|
||||
_, err = s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s.server.db.Exec("UPDATE characters SET time_played = $1 WHERE id = $2", timePlayed, s.charID)
|
||||
s.server.db.Exec("UPDATE characters SET cafe_time=cafe_time+$1 WHERE id=$2", sessionTime, s.charID)
|
||||
|
||||
treasureHuntUnregister(s)
|
||||
|
||||
if s.stage == nil {
|
||||
return
|
||||
@@ -243,7 +246,6 @@ func logoutPlayer(s *Session) {
|
||||
|
||||
removeSessionFromSemaphore(s)
|
||||
removeSessionFromStage(s)
|
||||
treasureHuntUnregister(s)
|
||||
|
||||
saveData, err := GetCharacterSaveData(s, s.charID)
|
||||
if err != nil {
|
||||
@@ -421,6 +423,9 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
for session := range stage.clients {
|
||||
count++
|
||||
hrp := uint16(1)
|
||||
gr := uint16(0)
|
||||
s.server.db.QueryRow("SELECT hrp, gr FROM characters WHERE id=$1", session.charID).Scan(&hrp, &gr)
|
||||
sessionStage := stringsupport.UTF8ToSJIS(session.stageID)
|
||||
sessionName := stringsupport.UTF8ToSJIS(session.Name)
|
||||
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
|
||||
@@ -433,8 +438,8 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
||||
resp.WriteBytes(make([]byte, 48))
|
||||
resp.WriteNullTerminatedBytes(sessionStage)
|
||||
resp.WriteNullTerminatedBytes(sessionName)
|
||||
resp.WriteUint16(999) // HR
|
||||
resp.WriteUint16(999) // GR
|
||||
resp.WriteUint16(hrp)
|
||||
resp.WriteUint16(gr)
|
||||
resp.WriteBytes([]byte{0x06, 0x10, 0x00}) // Unk
|
||||
}
|
||||
}
|
||||
@@ -443,12 +448,23 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
case 4: // Find Party
|
||||
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
|
||||
bf.ReadUint8()
|
||||
setting := bf.ReadUint8()
|
||||
maxResults := bf.ReadUint16()
|
||||
bf.ReadUint8()
|
||||
bf.ReadUint8()
|
||||
bf.Seek(2, 1)
|
||||
partyType := bf.ReadUint16()
|
||||
_ = bf.DataFromCurrent() // Restrictions
|
||||
rankRestriction := uint16(0)
|
||||
if setting >= 2 {
|
||||
bf.Seek(2, 1)
|
||||
rankRestriction = bf.ReadUint16()
|
||||
}
|
||||
targets := make([]uint16, 4)
|
||||
if setting >= 3 {
|
||||
bf.Seek(1, 1)
|
||||
lenTargets := int(bf.ReadUint8())
|
||||
for i := 0; i < lenTargets; i++ {
|
||||
targets[i] = bf.ReadUint16()
|
||||
}
|
||||
}
|
||||
var stagePrefix string
|
||||
switch partyType {
|
||||
case 0: // Public Bar
|
||||
@@ -468,31 +484,41 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(stage.id, stagePrefix) {
|
||||
sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}])
|
||||
sb3.Seek(4, 0)
|
||||
stageRankRestriction := sb3.ReadUint16()
|
||||
stageTarget := sb3.ReadUint16()
|
||||
if rankRestriction != 0xFFFF && stageRankRestriction < rankRestriction {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
sessionStage := stringsupport.UTF8ToSJIS(stage.id)
|
||||
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
|
||||
resp.WriteUint16(c.Port)
|
||||
|
||||
// TODO: This is half right, could be trimmed
|
||||
resp.WriteUint16(0)
|
||||
resp.WriteUint16(uint16(len(stage.clients)))
|
||||
resp.WriteUint16(0) // Static?
|
||||
resp.WriteUint16(0) // Unk
|
||||
resp.WriteUint16(uint16(len(stage.clients)))
|
||||
resp.WriteUint16(stage.maxPlayers)
|
||||
resp.WriteUint16(0)
|
||||
resp.WriteUint16(uint16(len(stage.clients)))
|
||||
//
|
||||
|
||||
resp.WriteUint16(uint16(len(sessionStage) + 1))
|
||||
resp.WriteUint8(1)
|
||||
resp.WriteUint16(0) // Num clients entered from stage
|
||||
resp.WriteUint16(stage.maxPlayers)
|
||||
resp.WriteUint8(1) // Static?
|
||||
resp.WriteUint8(uint8(len(sessionStage) + 1))
|
||||
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 0}])))
|
||||
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}])))
|
||||
resp.WriteBytes(make([]byte, 16))
|
||||
resp.WriteUint16(stageRankRestriction)
|
||||
resp.WriteUint16(stageTarget)
|
||||
resp.WriteBytes(make([]byte, 12))
|
||||
resp.WriteNullTerminatedBytes(sessionStage)
|
||||
resp.WriteBytes([]byte{0x00})
|
||||
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 0}])
|
||||
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pkt.SearchType == 1 || pkt.SearchType == 3) && count == 0 {
|
||||
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
resp.Seek(0, io.SeekStart)
|
||||
resp.WriteUint16(count)
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
@@ -641,86 +667,57 @@ func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem)
|
||||
var netcafe_points int
|
||||
err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafe_points)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err))
|
||||
}
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(uint32(netcafe_points))
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfUpdateCafepoint)
|
||||
var netcafe_points int
|
||||
err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafe_points)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to get plate data savedata from db", zap.Error(err))
|
||||
}
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(0)
|
||||
resp.WriteUint32(uint32(netcafe_points))
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfCheckDailyCafepoint)
|
||||
|
||||
// I am not sure exactly what this does, but all responses I have seen include this exact sequence of bytes
|
||||
// 1 daily, 5 daily halk pots, 3 point boosted quests, also adds 5 netcafe points but not sent to client
|
||||
// available once after midday every day
|
||||
|
||||
// get next midday
|
||||
var t = Time_static()
|
||||
year, month, day := t.Date()
|
||||
midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location())
|
||||
if t.After(midday) {
|
||||
midday = midday.Add(24 * time.Hour)
|
||||
}
|
||||
|
||||
// get time after which daily claiming would be valid from db
|
||||
var dailyTime time.Time
|
||||
err := s.server.db.QueryRow("SELECT COALESCE(daily_time, $2) FROM characters WHERE id = $1", s.charID, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)).Scan(&dailyTime)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to get daily_time savedata from db", zap.Error(err))
|
||||
}
|
||||
|
||||
if t.After(dailyTime) {
|
||||
// +5 netcafe points and setting next valid window
|
||||
_, err := s.server.db.Exec("UPDATE characters SET daily_time=$1, netcafe_points=netcafe_points::int + 5 WHERE id=$2", midday, s.charID)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update daily_time and netcafe_points savedata in db", zap.Error(err))
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01})
|
||||
} else {
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfCheckMonthlyItem(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfAcquireMonthlyItem(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp)
|
||||
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint16(0x000E)
|
||||
resp.WriteUint16(0x0001)
|
||||
resp.WriteUint16(0x0000)
|
||||
resp.WriteUint16(0x0000) // 0x0000 stops the vaguely annoying log in pop up
|
||||
resp.WriteUint32(0)
|
||||
resp.WriteUint32(0x5dddcbb3) // Timestamp
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
weekCurrentStart := TimeWeekStart()
|
||||
weekNextStart := TimeWeekNext()
|
||||
var total, redeemed, updated uint16
|
||||
var nextClaim time.Time
|
||||
err := s.server.db.QueryRow(fmt.Sprintf("SELECT %s_next FROM stamps WHERE character_id=$1", pkt.StampType), s.charID).Scan(&nextClaim)
|
||||
if err != nil {
|
||||
s.server.db.Exec("INSERT INTO stamps (character_id, hl_next, ex_next) VALUES ($1, $2, $2)", s.charID, weekNextStart)
|
||||
nextClaim = weekNextStart
|
||||
}
|
||||
if nextClaim.Before(weekCurrentStart) {
|
||||
s.server.db.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1, %s_next=$1 WHERE character_id=$2", pkt.StampType, pkt.StampType, pkt.StampType), weekNextStart, s.charID)
|
||||
updated = 1
|
||||
}
|
||||
s.server.db.QueryRow(fmt.Sprintf("SELECT %s_total, %s_redeemed FROM stamps WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(total)
|
||||
bf.WriteUint16(redeemed)
|
||||
bf.WriteUint16(updated)
|
||||
bf.WriteUint32(0) // Unk
|
||||
bf.WriteUint32(uint32(weekCurrentStart.Unix()))
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp)
|
||||
var total, redeemed uint16
|
||||
var tktStack mhfpacket.WarehouseStack
|
||||
if pkt.Unk1 == 0xA { // Yearly Sub Ex
|
||||
s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed)
|
||||
tktStack = mhfpacket.WarehouseStack{ItemID: 0x08A2, Quantity: 1}
|
||||
} else {
|
||||
s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
|
||||
if pkt.StampType == "hl" {
|
||||
tktStack = mhfpacket.WarehouseStack{ItemID: 0x065E, Quantity: 5}
|
||||
} else {
|
||||
tktStack = mhfpacket.WarehouseStack{ItemID: 0x065F, Quantity: 5}
|
||||
}
|
||||
}
|
||||
addWarehouseGift(s, "item", tktStack)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(total)
|
||||
bf.WriteUint16(redeemed)
|
||||
bf.WriteUint16(0)
|
||||
bf.WriteUint32(0) // Unk, but has possible values
|
||||
bf.WriteUint32(uint32(TimeWeekStart().Unix()))
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func getGookData(s *Session, cid uint32) (uint16, []byte) {
|
||||
var data []byte
|
||||
|
||||
239
server/channelserver/handlers_cafe.go
Normal file
239
server/channelserver/handlers_cafe.go
Normal file
@@ -0,0 +1,239 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"erupe-ce/common/byteframe"
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"go.uber.org/zap"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
func handleMsgMhfAcquireCafeItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfAcquireCafeItem)
|
||||
var netcafePoints uint32
|
||||
err := s.server.db.QueryRow("UPDATE characters SET netcafe_points = netcafe_points - $1 WHERE id = $2 RETURNING netcafe_points", pkt.PointCost, s.charID).Scan(&netcafePoints)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to get netcafe points from db", zap.Error(err))
|
||||
}
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(netcafePoints)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfUpdateCafepoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfUpdateCafepoint)
|
||||
var netcafePoints uint32
|
||||
err := s.server.db.QueryRow("SELECT COALESCE(netcafe_points, 0) FROM characters WHERE id = $1", s.charID).Scan(&netcafePoints)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to get netcate points from db", zap.Error(err))
|
||||
}
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(netcafePoints)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfCheckDailyCafepoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfCheckDailyCafepoint)
|
||||
|
||||
// I am not sure exactly what this does, but all responses I have seen include this exact sequence of bytes
|
||||
// 1 daily, 5 daily halk pots, 3 point boosted quests, also adds 5 netcafe points but not sent to client
|
||||
// available once after midday every day
|
||||
|
||||
// get next midday
|
||||
var t = Time_static()
|
||||
year, month, day := t.Date()
|
||||
midday := time.Date(year, month, day, 12, 0, 0, 0, t.Location())
|
||||
if t.After(midday) {
|
||||
midday = midday.Add(24 * time.Hour)
|
||||
}
|
||||
|
||||
// get time after which daily claiming would be valid from db
|
||||
var dailyTime time.Time
|
||||
err := s.server.db.QueryRow("SELECT COALESCE(daily_time, $2) FROM characters WHERE id = $1", s.charID, time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)).Scan(&dailyTime)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to get daily_time savedata from db", zap.Error(err))
|
||||
}
|
||||
|
||||
if t.After(dailyTime) {
|
||||
// +5 netcafe points and setting next valid window
|
||||
_, err := s.server.db.Exec("UPDATE characters SET daily_time=$1, netcafe_points=netcafe_points+5 WHERE id=$2", midday, s.charID)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update daily_time and netcafe_points savedata in db", zap.Error(err))
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01})
|
||||
} else {
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetCafeDuration)
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
var cafeTime uint32
|
||||
err := s.server.db.QueryRow("SELECT cafe_time FROM characters WHERE id = $1", s.charID).Scan(&cafeTime)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cafeTime = uint32(Time_Current_Adjusted().Unix()) - uint32(s.sessionStart) + cafeTime
|
||||
bf.WriteUint32(cafeTime) // Total cafe time
|
||||
bf.WriteUint16(0)
|
||||
ps.Uint16(bf, "Resets at next maintenance", true)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
type CafeBonus struct {
|
||||
ID uint32 `db:"id"`
|
||||
TimeReq uint32 `db:"time_req"`
|
||||
ItemType uint32 `db:"item_type"`
|
||||
ItemID uint32 `db:"item_id"`
|
||||
Quantity uint32 `db:"quantity"`
|
||||
Claimed bool `db:"claimed"`
|
||||
}
|
||||
|
||||
func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetCafeDurationBonusInfo)
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
var count uint32
|
||||
rows, err := s.server.db.Queryx(`
|
||||
SELECT cb.id, time_req, item_type, item_id, quantity,
|
||||
(
|
||||
SELECT count(*)
|
||||
FROM cafe_accepted ca
|
||||
WHERE cb.id = ca.cafe_id AND ca.character_id = $1
|
||||
)::int::bool AS claimed
|
||||
FROM cafebonus cb ORDER BY id ASC;`, s.charID)
|
||||
if err != nil {
|
||||
s.logger.Error("Error getting cafebonus", zap.Error(err))
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
} else {
|
||||
for rows.Next() {
|
||||
count++
|
||||
cafeBonus := &CafeBonus{}
|
||||
err = rows.StructScan(&cafeBonus)
|
||||
if err != nil {
|
||||
s.logger.Error("Error scanning cafebonus", zap.Error(err))
|
||||
}
|
||||
bf.WriteUint32(cafeBonus.TimeReq)
|
||||
bf.WriteUint32(cafeBonus.ItemType)
|
||||
bf.WriteUint32(cafeBonus.ItemID)
|
||||
bf.WriteUint32(cafeBonus.Quantity)
|
||||
bf.WriteBool(cafeBonus.Claimed)
|
||||
}
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(0)
|
||||
resp.WriteUint32(uint32(time.Now().Unix()))
|
||||
resp.WriteUint32(count)
|
||||
resp.WriteBytes(bf.Data())
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfReceiveCafeDurationBonus)
|
||||
bf := byteframe.NewByteFrame()
|
||||
var count uint32
|
||||
bf.WriteUint32(0)
|
||||
rows, err := s.server.db.Queryx(`
|
||||
SELECT c.id, time_req, item_type, item_id, quantity
|
||||
FROM cafebonus c
|
||||
WHERE (
|
||||
SELECT count(*)
|
||||
FROM cafe_accepted ca
|
||||
WHERE c.id = ca.cafe_id AND ca.character_id = $1
|
||||
) < 1 AND (
|
||||
SELECT ch.cafe_time + $2
|
||||
FROM characters ch
|
||||
WHERE ch.id = $1
|
||||
) >= time_req`, s.charID, Time_Current_Adjusted().Unix()-s.sessionStart)
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
} else {
|
||||
for rows.Next() {
|
||||
cafeBonus := &CafeBonus{}
|
||||
err = rows.StructScan(cafeBonus)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
bf.WriteUint32(cafeBonus.ID)
|
||||
bf.WriteUint32(cafeBonus.ItemType)
|
||||
bf.WriteUint32(cafeBonus.ItemID)
|
||||
bf.WriteUint32(cafeBonus.Quantity)
|
||||
}
|
||||
bf.Seek(0, io.SeekStart)
|
||||
bf.WriteUint32(count)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostCafeDurationBonusReceived)
|
||||
var cafeBonus CafeBonus
|
||||
for _, cbID := range pkt.CafeBonusID {
|
||||
err := s.server.db.QueryRow(`
|
||||
SELECT cb.id, item_type, quantity FROM cafebonus cb WHERE cb.id=$1
|
||||
`, cbID).Scan(&cafeBonus.ID, &cafeBonus.ItemType, &cafeBonus.Quantity)
|
||||
if err == nil {
|
||||
if cafeBonus.ItemType == 17 {
|
||||
s.server.db.Exec("UPDATE characters SET netcafe_points=netcafe_points+$1 WHERE id=$2", cafeBonus.Quantity, s.charID)
|
||||
}
|
||||
}
|
||||
s.server.db.Exec("INSERT INTO public.cafe_accepted VALUES ($1, $2)", cbID, s.charID)
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfStartBoostTime)
|
||||
bf := byteframe.NewByteFrame()
|
||||
boostLimit := Time_Current_Adjusted().Add(100 * time.Minute)
|
||||
s.server.db.Exec("UPDATE characters SET boost_time=$1 WHERE id=$2", boostLimit, s.charID)
|
||||
bf.WriteUint32(uint32(boostLimit.Unix()))
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetBoostTime)
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{})
|
||||
}
|
||||
|
||||
func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit)
|
||||
bf := byteframe.NewByteFrame()
|
||||
var boostLimit time.Time
|
||||
err := s.server.db.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.charID).Scan(&boostLimit)
|
||||
if err != nil {
|
||||
bf.WriteUint32(0)
|
||||
} else {
|
||||
bf.WriteUint32(uint32(boostLimit.Unix()))
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetBoostRight)
|
||||
var boostLimit time.Time
|
||||
err := s.server.db.QueryRow("SELECT boost_time FROM characters WHERE id=$1", s.charID).Scan(&boostLimit)
|
||||
if err != nil {
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
return
|
||||
}
|
||||
if boostLimit.Unix() > Time_Current_Adjusted().Unix() {
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
||||
} else {
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x02})
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostBoostTimeQuestReturn)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {}
|
||||
@@ -165,6 +165,66 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
fmt.Printf("Got chat message: %+v\n", chatMessage)
|
||||
|
||||
// Flush all objects and users and reload
|
||||
if strings.HasPrefix(chatMessage.Message, "!reload") {
|
||||
sendServerChatMessage(s, "Reloading players...")
|
||||
var temp mhfpacket.MHFPacket
|
||||
deleteNotif := byteframe.NewByteFrame()
|
||||
for _, object := range s.stage.objects {
|
||||
if object.ownerCharID == s.charID {
|
||||
continue
|
||||
}
|
||||
temp = &mhfpacket.MsgSysDeleteObject{ObjID: object.id}
|
||||
deleteNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(deleteNotif, s.clientContext)
|
||||
}
|
||||
for _, session := range s.server.sessions {
|
||||
if s == session {
|
||||
continue
|
||||
}
|
||||
temp = &mhfpacket.MsgSysDeleteUser{CharID: session.charID}
|
||||
deleteNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(deleteNotif, s.clientContext)
|
||||
}
|
||||
deleteNotif.WriteUint16(0x0010)
|
||||
s.QueueSend(deleteNotif.Data())
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
reloadNotif := byteframe.NewByteFrame()
|
||||
for _, session := range s.server.sessions {
|
||||
if s == session {
|
||||
continue
|
||||
}
|
||||
temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID}
|
||||
reloadNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(reloadNotif, s.clientContext)
|
||||
for i := 0; i < 3; i++ {
|
||||
temp = &mhfpacket.MsgSysNotifyUserBinary{
|
||||
CharID: session.charID,
|
||||
BinaryType: uint8(i + 1),
|
||||
}
|
||||
reloadNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(reloadNotif, s.clientContext)
|
||||
}
|
||||
}
|
||||
for _, obj := range s.stage.objects {
|
||||
if obj.ownerCharID == s.charID {
|
||||
continue
|
||||
}
|
||||
temp = &mhfpacket.MsgSysDuplicateObject{
|
||||
ObjID: obj.id,
|
||||
X: obj.x,
|
||||
Y: obj.y,
|
||||
Z: obj.z,
|
||||
Unk0: 0,
|
||||
OwnerCharID: obj.ownerCharID,
|
||||
}
|
||||
reloadNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(reloadNotif, s.clientContext)
|
||||
}
|
||||
reloadNotif.WriteUint16(0x0010)
|
||||
s.QueueSend(reloadNotif.Data())
|
||||
}
|
||||
|
||||
// Set account rights
|
||||
if strings.HasPrefix(chatMessage.Message, "!rights") {
|
||||
var v uint32
|
||||
@@ -180,7 +240,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
// Discord integration
|
||||
if chatMessage.Type == binpacket.ChatTypeLocal || chatMessage.Type == binpacket.ChatTypeParty {
|
||||
if (pkt.BroadcastType == BroadcastTypeStage && s.stage.id == "sl1Ns200p0a0u0") || pkt.BroadcastType == BroadcastTypeWorld {
|
||||
s.server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message)
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
|
||||
s.logger.Fatal("Failed to update savedata in db", zap.Error(err))
|
||||
}
|
||||
s.logger.Info("Wrote recompressed savedata back to DB.")
|
||||
dumpSaveData(s, pkt.RawDataPayload, "")
|
||||
dumpSaveData(s, pkt.RawDataPayload, "savedata")
|
||||
|
||||
_, err = s.server.db.Exec("UPDATE characters SET weapon_type=$1 WHERE id=$2", uint16(decompressedData[128789]), s.charID)
|
||||
if err != nil {
|
||||
@@ -275,8 +275,8 @@ func dumpSaveData(s *Session, data []byte, suffix string) {
|
||||
if !s.server.erupeConfig.DevModeOptions.SaveDumps.Enabled {
|
||||
return
|
||||
} else {
|
||||
dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%s_", s.Name))
|
||||
path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%s_", s.Name), fmt.Sprintf("%d_%s_%s%s.bin", s.charID, s.Name, Time_Current().Format("2006-01-02_15.04.05"), suffix))
|
||||
dir := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name))
|
||||
path := filepath.Join(s.server.erupeConfig.DevModeOptions.SaveDumps.OutputDir, fmt.Sprintf("%d_%s", s.charID, s.Name), fmt.Sprintf("%d_%s_%s.bin", s.charID, s.Name, suffix))
|
||||
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
os.Mkdir(dir, os.ModeDir)
|
||||
@@ -297,10 +297,11 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
var data []byte
|
||||
|
||||
err := s.server.db.QueryRow("SELECT savedata FROM characters WHERE id = $1", s.charID).Scan(&data)
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to get savedata from db", zap.Error(err))
|
||||
if err != nil || len(data) == 0 {
|
||||
s.logger.Warn(fmt.Sprintf("Failed to load savedata (CID: %d)", s.charID), zap.Error(err))
|
||||
s.rawConn.Close() // Terminate the connection
|
||||
return
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
|
||||
@@ -310,11 +311,11 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
bf := byteframe.NewByteFrameFromBytes(decompSaveData)
|
||||
bf.Seek(88, io.SeekStart)
|
||||
binary1 := bf.ReadNullTerminatedBytes()
|
||||
name := bf.ReadNullTerminatedBytes()
|
||||
s.server.userBinaryPartsLock.Lock()
|
||||
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: 1}] = append(binary1, []byte{0x00}...)
|
||||
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: 1}] = append(name, []byte{0x00}...)
|
||||
s.server.userBinaryPartsLock.Unlock()
|
||||
s.Name = stringsupport.SJISToUTF8(binary1)
|
||||
s.Name = stringsupport.SJISToUTF8(name)
|
||||
}
|
||||
|
||||
func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
@@ -2,87 +2,45 @@ package channelserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type Character struct {
|
||||
ID uint32 `db:"id"`
|
||||
IsFemale bool `db:"is_female"`
|
||||
IsNewCharacter bool `db:"is_new_character"`
|
||||
Name string `db:"name"`
|
||||
UnkDescString string `db:"unk_desc_string"`
|
||||
HRP uint16 `db:"hrp"`
|
||||
GR uint16 `db:"gr"`
|
||||
WeaponType uint16 `db:"weapon_type"`
|
||||
LastLogin uint32 `db:"last_login"`
|
||||
type Player struct {
|
||||
CharName string
|
||||
QuestID int
|
||||
}
|
||||
|
||||
var weapons = []string{
|
||||
"<:gs:970861408227049477>",
|
||||
"<:hbg:970861408281563206>",
|
||||
"<:hm:970861408239628308>",
|
||||
"<:lc:970861408298352660>",
|
||||
"<:sns:970861408319315988>",
|
||||
"<:lbg:970861408327725137>",
|
||||
"<:ds:970861408277368883>",
|
||||
"<:ls:970861408319311872>",
|
||||
"<:hh:970861408222863400>",
|
||||
"<:gl:970861408327720980>",
|
||||
"<:bw:970861408294174780>",
|
||||
"<:tf:970861408424177744>",
|
||||
"<:sw:970861408340283472>",
|
||||
"<:ms:970861408411594842>",
|
||||
}
|
||||
func getPlayerSlice(s *Server) []Player {
|
||||
var p []Player
|
||||
var questIndex int
|
||||
|
||||
func (s *Server) getCharacterForUser(uid int) (*Character, error) {
|
||||
character := Character{}
|
||||
err := s.db.Get(&character, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE id = $1", uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
for _, channel := range s.Channels {
|
||||
for _, stage := range channel.stages {
|
||||
if len(stage.clients) == 0 {
|
||||
continue
|
||||
}
|
||||
questID := 0
|
||||
if stage.isQuest() {
|
||||
questIndex++
|
||||
questID = questIndex
|
||||
}
|
||||
for client := range stage.clients {
|
||||
p = append(p, Player{
|
||||
CharName: client.Name,
|
||||
QuestID: questID,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &character, nil
|
||||
return p
|
||||
}
|
||||
|
||||
func CountChars(s *Server) string {
|
||||
count := 0
|
||||
for _, stage := range s.stages {
|
||||
count += len(stage.clients)
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("Server [%s]: %d players;", s.name, count)
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
type ListPlayer struct {
|
||||
CharName string
|
||||
InQuest bool
|
||||
WeaponEmoji string
|
||||
QuestEmoji string
|
||||
StageName string
|
||||
}
|
||||
|
||||
func (p *ListPlayer) toString(length int) string {
|
||||
status := ""
|
||||
if p.InQuest {
|
||||
status = "(in quest " + p.QuestEmoji + ")"
|
||||
} else {
|
||||
status = p.StageName
|
||||
}
|
||||
|
||||
missingSpace := length - len(p.CharName)
|
||||
whitespaces := strings.Repeat(" ", missingSpace+5)
|
||||
|
||||
return fmt.Sprintf("%s %s %s %s", p.WeaponEmoji, p.CharName, whitespaces, status)
|
||||
}
|
||||
|
||||
func getPlayerList(s *Server) ([]ListPlayer, int) {
|
||||
list := []ListPlayer{}
|
||||
func getCharacterList(s *Server) string {
|
||||
questEmojis := []string{
|
||||
":person_in_lotus_position:",
|
||||
":white_circle:",
|
||||
":red_circle:",
|
||||
":blue_circle:",
|
||||
@@ -91,255 +49,41 @@ func getPlayerList(s *Server) ([]ListPlayer, int) {
|
||||
":purple_circle:",
|
||||
":yellow_circle:",
|
||||
":orange_circle:",
|
||||
":black_circle:",
|
||||
}
|
||||
|
||||
bigNameLen := 0
|
||||
playerSlice := getPlayerSlice(s)
|
||||
|
||||
for _, stage := range s.stages {
|
||||
if len(stage.clients) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
questEmoji := ":black_circle:"
|
||||
|
||||
if len(questEmojis) > 0 {
|
||||
questEmoji = questEmojis[len(questEmojis)-1]
|
||||
questEmojis = questEmojis[:len(questEmojis)-1]
|
||||
}
|
||||
|
||||
isQuest := stage.isQuest()
|
||||
for client := range stage.clients {
|
||||
char, err := s.getCharacterForUser(int(client.charID))
|
||||
if err == nil {
|
||||
if len(char.Name) > bigNameLen {
|
||||
bigNameLen = len(char.Name)
|
||||
}
|
||||
|
||||
list = append(list, ListPlayer{
|
||||
CharName: char.Name,
|
||||
InQuest: isQuest,
|
||||
QuestEmoji: questEmoji,
|
||||
WeaponEmoji: weapons[char.WeaponType],
|
||||
StageName: stage.GetName(),
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list, bigNameLen
|
||||
}
|
||||
|
||||
func PlayerList(s *Server) string {
|
||||
list := ""
|
||||
count := 0
|
||||
listPlayers, bigNameLen := getPlayerList(s)
|
||||
|
||||
sort.SliceStable(listPlayers, func(i, j int) bool {
|
||||
return listPlayers[i].CharName < listPlayers[j].CharName
|
||||
sort.SliceStable(playerSlice, func(i, j int) bool {
|
||||
return playerSlice[i].QuestID < playerSlice[j].QuestID
|
||||
})
|
||||
|
||||
for _, lp := range listPlayers {
|
||||
list += lp.toString(bigNameLen) + "\n"
|
||||
count++
|
||||
message := fmt.Sprintf("===== Online: %d =====\n", len(playerSlice))
|
||||
for _, player := range playerSlice {
|
||||
message += fmt.Sprintf("%s %s", questEmojis[player.QuestID], player.CharName)
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("<:SnS:822963937360347148> Frontier Hunters Online: [%s ] <:switcha:822963906401533992> \n============== Total %d =============\n", s.name, count)
|
||||
message += list
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
func debug(s *Server) string {
|
||||
list := ""
|
||||
|
||||
for _, stage := range s.stages {
|
||||
if !stage.isQuest() && len(stage.objects) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
list += fmt.Sprintf(" -> Stage: %s StageId: %s\n", stage.GetName(), stage.id)
|
||||
isQuest := "false"
|
||||
hasDeparted := "false"
|
||||
|
||||
if stage.isQuest() {
|
||||
isQuest = "true"
|
||||
}
|
||||
|
||||
list += fmt.Sprintf(" '-> isQuest: %s\n", isQuest)
|
||||
|
||||
if stage.isQuest() {
|
||||
if len(stage.clients) > 0 {
|
||||
hasDeparted = "true"
|
||||
}
|
||||
|
||||
list += fmt.Sprintf(" '-> isDeparted: %s\n", hasDeparted)
|
||||
list += fmt.Sprintf(" '-> reserveSlots (%d/%d)\n", len(stage.reservedClientSlots), stage.maxPlayers)
|
||||
|
||||
for charid, _ := range stage.reservedClientSlots {
|
||||
char, err := s.getCharacterForUser(int(charid))
|
||||
if err == nil {
|
||||
list += fmt.Sprintf(" '-> %s\n", char.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list += " '-> objects: \n"
|
||||
for _, obj := range stage.objects {
|
||||
objInfo := fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z)
|
||||
list += fmt.Sprintf(" '-> ObjectId: %d - %s\n", obj.id, objInfo)
|
||||
}
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("Objects in Server: [%s ]\n", s.name)
|
||||
message += list
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
func questlist(s *Server) string {
|
||||
list := ""
|
||||
|
||||
for _, stage := range s.stages {
|
||||
if !stage.isQuest() {
|
||||
continue
|
||||
}
|
||||
|
||||
hasDeparted := ""
|
||||
if len(stage.clients) > 0 {
|
||||
hasDeparted = " - departed"
|
||||
}
|
||||
list += fmt.Sprintf(" '-> StageId: %s (%d/%d) %s - %s\n", stage.id, len(stage.reservedClientSlots), stage.maxPlayers, hasDeparted, stage.createdAt)
|
||||
|
||||
for charid, _ := range stage.reservedClientSlots {
|
||||
char, err := s.getCharacterForUser(int(charid))
|
||||
if err == nil {
|
||||
list += fmt.Sprintf(" '-> %s\n", char.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message := fmt.Sprintf("Quests in Server: [%s ]\n", s.name)
|
||||
message += list
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
func removeStageById(s *Server, stageId string) string {
|
||||
if s.stages[stageId] != nil {
|
||||
delete(s.stages, stageId)
|
||||
return "Stage deleted!"
|
||||
}
|
||||
|
||||
return "Stage not found!"
|
||||
}
|
||||
|
||||
func cleanStr(str string) string {
|
||||
return strings.ToLower(strings.Trim(str, " "))
|
||||
}
|
||||
|
||||
func getCharInfo(server *Server, charName string) string {
|
||||
var s *Stage
|
||||
var c *Session
|
||||
|
||||
for _, stage := range server.stages {
|
||||
for client := range stage.clients {
|
||||
|
||||
if client.Name == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if cleanStr(client.Name) == cleanStr(charName) {
|
||||
s = stage
|
||||
c = client
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if s == nil {
|
||||
return "Character not found"
|
||||
}
|
||||
|
||||
objInfo := ""
|
||||
|
||||
obj := server.FindObjectByChar(c.charID)
|
||||
// server.logger.Info("Found object: %+v", zap.Object("obj", obj))
|
||||
|
||||
if obj != nil {
|
||||
objInfo = fmt.Sprintf("X,Y,Z: %f %f %f", obj.x, obj.y, obj.z)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Character: %s\nStage: %s\nStageId: %s\n%s", c.Name, s.GetName(), s.id, objInfo)
|
||||
}
|
||||
|
||||
func (s *Server) isDiscordAdmin(ds *discordgo.Session, m *discordgo.MessageCreate) bool {
|
||||
for _, role := range m.Member.Roles {
|
||||
for _, id := range s.erupeConfig.Discord.DevRoles {
|
||||
if id == role {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// onDiscordMessage handles receiving messages from discord and forwarding them ingame.
|
||||
func (s *Server) onDiscordMessage(ds *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
// Ignore messages from our bot, or ones that are not in the correct channel.
|
||||
if m.Author.ID == ds.State.User.ID || !s.enable {
|
||||
if m.Author.Bot || m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID {
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore other channels in devMode
|
||||
if s.erupeConfig.Discord.DevMode && m.ChannelID != s.erupeConfig.Discord.RealtimeChannelID {
|
||||
return
|
||||
}
|
||||
|
||||
args := strings.Split(m.Content, " ")
|
||||
commandName := args[0]
|
||||
|
||||
// Move to slash commadns
|
||||
if commandName == "!players" {
|
||||
ds.ChannelMessageSend(m.ChannelID, PlayerList(s))
|
||||
return
|
||||
}
|
||||
|
||||
if commandName == "-char" {
|
||||
if len(args) < 2 {
|
||||
ds.ChannelMessageSend(m.ChannelID, "Usage: !char <char name>")
|
||||
return
|
||||
paddedName := strings.TrimSpace(strings.Map(func(r rune) rune {
|
||||
if r > unicode.MaxASCII {
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}, m.Author.Username))
|
||||
|
||||
charName := strings.Join(args[1:], " ")
|
||||
ds.ChannelMessageSend(m.ChannelID, getCharInfo(s, charName))
|
||||
return
|
||||
for i := 0; i < 8-len(m.Author.Username); i++ {
|
||||
paddedName += " "
|
||||
}
|
||||
|
||||
if commandName == "!debug" && s.isDiscordAdmin(ds, m) {
|
||||
ds.ChannelMessageSend(m.ChannelID, debug(s))
|
||||
return
|
||||
}
|
||||
|
||||
if commandName == "!questlist" && s.isDiscordAdmin(ds, m) {
|
||||
ds.ChannelMessageSend(m.ChannelID, questlist(s))
|
||||
return
|
||||
}
|
||||
|
||||
if commandName == "!remove-stage" && s.isDiscordAdmin(ds, m) {
|
||||
if len(args) < 2 {
|
||||
ds.ChannelMessageSend(m.ChannelID, "Usage: !remove-stage <stage id>")
|
||||
return
|
||||
}
|
||||
|
||||
stageId := strings.Join(args[1:], " ")
|
||||
ds.ChannelMessageSend(m.ChannelID, removeStageById(s, stageId))
|
||||
return
|
||||
}
|
||||
|
||||
if m.ChannelID == s.erupeConfig.Discord.RealtimeChannelID {
|
||||
message := fmt.Sprintf("[DISCORD] %s: %s", m.Author.Username, m.Content)
|
||||
s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message))
|
||||
}
|
||||
message := fmt.Sprintf("[D] %s > %s", paddedName, m.Content)
|
||||
s.BroadcastChatMessage(s.discordBot.NormalizeDiscordMessage(message))
|
||||
}
|
||||
|
||||
@@ -303,34 +303,6 @@ func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetBoostTime)
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{})
|
||||
updateRights(s)
|
||||
}
|
||||
|
||||
func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit)
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetBoostRight)
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostBoostTimeQuestReturn)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfSetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
@@ -1892,11 +1892,25 @@ func handleMsgMhfSetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfCheckMonthlyItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfCheckMonthlyItem)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
|
||||
// TODO: Implement month-by-month tracker, 0 = Not claimed, 1 = Claimed
|
||||
}
|
||||
|
||||
func handleMsgMhfAcquireMonthlyItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfAcquireMonthlyItem)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateInvGuild)
|
||||
stubEnumerateNoResults(s, pkt.AckHandle)
|
||||
}
|
||||
|
||||
func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfOperationInvGuild)
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfUpdateGuildcard(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
@@ -5,11 +5,37 @@ import (
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/common/stringsupport"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
const warehouseNamesQuery = `
|
||||
SELECT
|
||||
COALESCE(item0name, ''),
|
||||
COALESCE(item1name, ''),
|
||||
COALESCE(item2name, ''),
|
||||
COALESCE(item3name, ''),
|
||||
COALESCE(item4name, ''),
|
||||
COALESCE(item5name, ''),
|
||||
COALESCE(item6name, ''),
|
||||
COALESCE(item7name, ''),
|
||||
COALESCE(item8name, ''),
|
||||
COALESCE(item9name, ''),
|
||||
COALESCE(equip0name, ''),
|
||||
COALESCE(equip1name, ''),
|
||||
COALESCE(equip2name, ''),
|
||||
COALESCE(equip3name, ''),
|
||||
COALESCE(equip4name, ''),
|
||||
COALESCE(equip5name, ''),
|
||||
COALESCE(equip6name, ''),
|
||||
COALESCE(equip7name, ''),
|
||||
COALESCE(equip8name, ''),
|
||||
COALESCE(equip9name, '')
|
||||
FROM warehouse
|
||||
`
|
||||
|
||||
func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfUpdateInterior)
|
||||
_, err := s.server.db.Exec("UPDATE characters SET house=$1 WHERE id=$2", pkt.InteriorData, s.charID)
|
||||
@@ -66,7 +92,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
case 3:
|
||||
house := HouseData{}
|
||||
row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name=$1", pkt.Name)
|
||||
row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name ILIKE $1", fmt.Sprintf(`%%%s%%`, pkt.Name))
|
||||
err := row.StructScan(&house)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -341,8 +367,190 @@ func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfOperateWarehouse)
|
||||
var t int
|
||||
err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t)
|
||||
if err != nil {
|
||||
s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID)
|
||||
}
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(pkt.Operation)
|
||||
switch pkt.Operation {
|
||||
case 0:
|
||||
var count uint8
|
||||
itemNames := make([]string, 10)
|
||||
equipNames := make([]string, 10)
|
||||
s.server.db.QueryRow(fmt.Sprintf("%s WHERE character_id=$1", warehouseNamesQuery), s.charID).Scan(&itemNames[0],
|
||||
&itemNames[1], &itemNames[2], &itemNames[3], &itemNames[4], &itemNames[5], &itemNames[6], &itemNames[7], &itemNames[8], &itemNames[9], &equipNames[0],
|
||||
&equipNames[1], &equipNames[2], &equipNames[3], &equipNames[4], &equipNames[5], &equipNames[6], &equipNames[7], &equipNames[8], &equipNames[9])
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint16(10000) // Usages
|
||||
temp := byteframe.NewByteFrame()
|
||||
for i, name := range itemNames {
|
||||
if len(name) > 0 {
|
||||
count++
|
||||
temp.WriteUint8(0)
|
||||
temp.WriteUint8(uint8(i))
|
||||
ps.Uint8(temp, name, true)
|
||||
}
|
||||
}
|
||||
for i, name := range equipNames {
|
||||
if len(name) > 0 {
|
||||
count++
|
||||
temp.WriteUint8(1)
|
||||
temp.WriteUint8(uint8(i))
|
||||
ps.Uint8(temp, name, true)
|
||||
}
|
||||
}
|
||||
bf.WriteUint8(count)
|
||||
bf.WriteBytes(temp.Data())
|
||||
case 1:
|
||||
bf.WriteUint8(0)
|
||||
case 2:
|
||||
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%dname=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), pkt.Name, s.charID)
|
||||
case 3:
|
||||
bf.WriteUint32(0) // Usage renewal time, >1 = disabled
|
||||
bf.WriteUint16(10000) // Usages
|
||||
case 4:
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint16(10000) // Usages
|
||||
bf.WriteUint8(0)
|
||||
}
|
||||
// Opcodes
|
||||
// 0 = Get box names
|
||||
// 1 = Commit usage
|
||||
// 2 = Rename
|
||||
// 3 = Get usage limit
|
||||
// 4 = Get gift box names (doesn't do anything?)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func addWarehouseGift(s *Session, boxType string, giftStack mhfpacket.WarehouseStack) {
|
||||
giftBox := getWarehouseBox(s, boxType, 10)
|
||||
if boxType == "item" {
|
||||
exists := false
|
||||
for i, stack := range giftBox {
|
||||
if stack.ItemID == giftStack.ItemID {
|
||||
exists = true
|
||||
giftBox[i].Quantity += giftStack.Quantity
|
||||
break
|
||||
}
|
||||
}
|
||||
if exists == false {
|
||||
giftBox = append(giftBox, giftStack)
|
||||
}
|
||||
} else {
|
||||
giftBox = append(giftBox, giftStack)
|
||||
}
|
||||
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s10=$1 WHERE character_id=$2", boxType), boxToBytes(giftBox, boxType), s.charID)
|
||||
}
|
||||
|
||||
func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.WarehouseStack {
|
||||
var data []byte
|
||||
s.server.db.QueryRow(fmt.Sprintf("SELECT %s%d FROM warehouse WHERE character_id=$1", boxType, boxIndex), s.charID).Scan(&data)
|
||||
if len(data) > 0 {
|
||||
box := byteframe.NewByteFrameFromBytes(data)
|
||||
numStacks := box.ReadUint16()
|
||||
stacks := make([]mhfpacket.WarehouseStack, numStacks)
|
||||
for i := 0; i < int(numStacks); i++ {
|
||||
if boxType == "item" {
|
||||
stacks[i].ID = box.ReadUint32()
|
||||
stacks[i].Index = box.ReadUint16()
|
||||
stacks[i].ItemID = box.ReadUint16()
|
||||
stacks[i].Quantity = box.ReadUint16()
|
||||
box.ReadUint16()
|
||||
} else {
|
||||
stacks[i].ID = box.ReadUint32()
|
||||
stacks[i].Index = box.ReadUint16()
|
||||
stacks[i].EquipType = box.ReadUint16()
|
||||
stacks[i].ItemID = box.ReadUint16()
|
||||
stacks[i].Data = box.ReadBytes(56)
|
||||
}
|
||||
}
|
||||
return stacks
|
||||
} else {
|
||||
return make([]mhfpacket.WarehouseStack, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func boxToBytes(stacks []mhfpacket.WarehouseStack, boxType string) []byte {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(uint16(len(stacks)))
|
||||
for i, stack := range stacks {
|
||||
if boxType == "item" {
|
||||
bf.WriteUint32(stack.ID)
|
||||
bf.WriteUint16(uint16(i + 1))
|
||||
bf.WriteUint16(stack.ItemID)
|
||||
bf.WriteUint16(stack.Quantity)
|
||||
bf.WriteUint16(0)
|
||||
} else {
|
||||
bf.WriteUint32(stack.ID)
|
||||
bf.WriteUint16(uint16(i + 1))
|
||||
bf.WriteUint16(stack.EquipType)
|
||||
bf.WriteUint16(stack.ItemID)
|
||||
bf.WriteBytes(stack.Data)
|
||||
}
|
||||
}
|
||||
bf.WriteUint16(0)
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse)
|
||||
box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex)
|
||||
if len(box) > 0 {
|
||||
doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box, pkt.BoxType))
|
||||
} else {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse)
|
||||
box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex)
|
||||
// Update existing stacks
|
||||
var newStacks []mhfpacket.WarehouseStack
|
||||
for _, update := range pkt.Updates {
|
||||
exists := false
|
||||
if pkt.BoxType == "item" {
|
||||
for i, stack := range box {
|
||||
if stack.Index == update.Index {
|
||||
exists = true
|
||||
box[i].Quantity = update.Quantity
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i, stack := range box {
|
||||
if stack.Index == update.Index {
|
||||
exists = true
|
||||
box[i].ItemID = update.ItemID
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if exists == false {
|
||||
newStacks = append(newStacks, update)
|
||||
}
|
||||
}
|
||||
// Append new stacks
|
||||
for _, stack := range newStacks {
|
||||
box = append(box, stack)
|
||||
}
|
||||
// Slice empty stacks
|
||||
var cleanedBox []mhfpacket.WarehouseStack
|
||||
for _, stack := range box {
|
||||
if pkt.BoxType == "item" {
|
||||
if stack.Quantity > 0 {
|
||||
cleanedBox = append(cleanedBox, stack)
|
||||
}
|
||||
} else {
|
||||
if stack.ItemID != 0 {
|
||||
cleanedBox = append(cleanedBox, stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox, pkt.BoxType), s.charID)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func handleMsgMhfLoadPartner(s *Session, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfSavePartner(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfSavePartner)
|
||||
|
||||
dumpSaveData(s, pkt.RawDataPayload, "_partner")
|
||||
dumpSaveData(s, pkt.RawDataPayload, "partner")
|
||||
|
||||
_, err := s.server.db.Exec("UPDATE characters SET partner=$1 WHERE id=$2", pkt.RawDataPayload, s.charID)
|
||||
if err != nil {
|
||||
@@ -75,7 +75,7 @@ func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfSaveHunterNavi)
|
||||
|
||||
dumpSaveData(s, pkt.RawDataPayload, "_hunternavi")
|
||||
dumpSaveData(s, pkt.RawDataPayload, "hunternavi")
|
||||
|
||||
if pkt.IsDataDiff {
|
||||
var data []byte
|
||||
@@ -121,10 +121,30 @@ func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
func handleMsgMhfMercenaryHuntdata(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfMercenaryHuntdata)
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0x0A))
|
||||
if pkt.Unk0 == 1 {
|
||||
// Format:
|
||||
// uint8 Hunts
|
||||
// struct Hunt
|
||||
// uint32 HuntID
|
||||
// uint32 MonID
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 1))
|
||||
} else {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 0))
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateMercenaryLog(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfEnumerateMercenaryLog(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateMercenaryLog)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0)
|
||||
// Format:
|
||||
// struct Log
|
||||
// uint32 Timestamp
|
||||
// []byte Name (len 18)
|
||||
// uint8 Unk
|
||||
// uint8 Unk
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfCreateMercenary)
|
||||
@@ -208,6 +228,7 @@ func handleMsgMhfLoadOtomoAirou(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfSaveOtomoAirou)
|
||||
dumpSaveData(s, pkt.RawDataPayload, "otomoairou")
|
||||
decomp, err := nullcomp.Decompress(pkt.RawDataPayload[1:])
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to decompress airou", zap.Error(err))
|
||||
|
||||
@@ -11,7 +11,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
|
||||
s.server.raviente.Lock()
|
||||
switch pkt.SemaphoreID {
|
||||
case 3:
|
||||
case 4:
|
||||
resp := byteframe.NewByteFrame()
|
||||
size := 6
|
||||
for i := 0; i < len(bf.Data())-1; i += size {
|
||||
@@ -49,7 +49,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
resp.WriteUint8(0)
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
case 4:
|
||||
case 5:
|
||||
resp := byteframe.NewByteFrame()
|
||||
size := 6
|
||||
for i := 0; i < len(bf.Data())-1; i += size {
|
||||
@@ -74,7 +74,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
resp.WriteUint8(0)
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
case 5:
|
||||
case 6:
|
||||
resp := byteframe.NewByteFrame()
|
||||
size := 6
|
||||
for i := 0; i < len(bf.Data())-1; i += size {
|
||||
@@ -242,15 +242,15 @@ func handleMsgSysLoadRegister(s *Session, p mhfpacket.MHFPacket) {
|
||||
func (s *Session) notifyRavi() {
|
||||
var temp mhfpacket.MHFPacket
|
||||
raviNotif := byteframe.NewByteFrame()
|
||||
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 3}
|
||||
raviNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(raviNotif, s.clientContext)
|
||||
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 4}
|
||||
raviNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(raviNotif, s.clientContext)
|
||||
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 5}
|
||||
raviNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(raviNotif, s.clientContext)
|
||||
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: 6}
|
||||
raviNotif.WriteUint16(uint16(temp.Opcode()))
|
||||
temp.Build(raviNotif, s.clientContext)
|
||||
raviNotif.WriteUint16(0x0010) // End it.
|
||||
sema := getRaviSemaphore(s)
|
||||
if sema != "" {
|
||||
@@ -262,7 +262,7 @@ func (s *Session) notifyRavi() {
|
||||
|
||||
func getRaviSemaphore(s *Session) string {
|
||||
for _, semaphore := range s.server.semaphore {
|
||||
if strings.HasPrefix(semaphore.id_semaphore, "hs_l0u3B5") && strings.HasSuffix(semaphore.id_semaphore, "3") {
|
||||
if strings.HasPrefix(semaphore.id_semaphore, "hs_l0u3B5") && strings.HasSuffix(semaphore.id_semaphore, "4") {
|
||||
return semaphore.id_semaphore
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
@@ -17,7 +19,19 @@ func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) {
|
||||
if err != nil {
|
||||
s.logger.Fatal("Failed to update rengokudata savedata in db", zap.Error(err))
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
|
||||
bf.Seek(71, 0)
|
||||
maxStageMp := bf.ReadUint32()
|
||||
maxScoreMp := bf.ReadUint32()
|
||||
bf.Seek(4, 1)
|
||||
maxStageSp := bf.ReadUint32()
|
||||
maxScoreSp := bf.ReadUint32()
|
||||
var t int
|
||||
err = s.server.db.QueryRow("SELECT character_id FROM rengoku_score WHERE character_id=$1", s.charID).Scan(&t)
|
||||
if err != nil {
|
||||
s.server.db.Exec("INSERT INTO rengoku_score (character_id) VALUES ($1)", s.charID)
|
||||
}
|
||||
s.server.db.Exec("UPDATE rengoku_score SET max_stages_mp=$1, max_points_mp=$2, max_stages_sp=$3, max_points_sp=$4 WHERE character_id=$5", maxStageMp, maxScoreMp, maxStageSp, maxScoreSp, s.charID)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
@@ -81,16 +95,191 @@ func handleMsgMhfGetRengokuBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
const rengokuScoreQuery = `
|
||||
SELECT max_stages_mp, max_points_mp, max_stages_sp, max_points_sp, c.name, gc.guild_id
|
||||
FROM rengoku_score rs
|
||||
LEFT JOIN characters c ON c.id = rs.character_id
|
||||
LEFT JOIN guild_characters gc ON gc.character_id = rs.character_id
|
||||
`
|
||||
|
||||
type RengokuScore struct {
|
||||
Name string `db:"name"`
|
||||
GuildID int `db:"guild_id"`
|
||||
MaxStagesMP uint32 `db:"max_stages_mp"`
|
||||
MaxPointsMP uint32 `db:"max_points_mp"`
|
||||
MaxStagesSP uint32 `db:"max_stages_sp"`
|
||||
MaxPointsSP uint32 `db:"max_points_sp"`
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateRengokuRanking)
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
|
||||
guild, _ := GetGuildInfoByCharacterId(s, s.charID)
|
||||
isApplicant, _ := guild.HasApplicationForCharID(s, s.charID)
|
||||
if isApplicant {
|
||||
guild = nil
|
||||
}
|
||||
|
||||
var score RengokuScore
|
||||
i := uint32(1)
|
||||
bf := byteframe.NewByteFrame()
|
||||
scoreData := byteframe.NewByteFrame()
|
||||
switch pkt.Leaderboard {
|
||||
case 0: // Max stage overall MP
|
||||
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_mp DESC", rengokuScoreQuery))
|
||||
for rows.Next() {
|
||||
rows.StructScan(&score)
|
||||
if score.Name == s.Name {
|
||||
bf.WriteUint32(i)
|
||||
bf.WriteUint32(score.MaxStagesMP)
|
||||
ps.Uint8(bf, s.Name, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
}
|
||||
scoreData.WriteUint32(i)
|
||||
scoreData.WriteUint32(score.MaxStagesMP)
|
||||
ps.Uint8(scoreData, score.Name, true)
|
||||
ps.Uint8(scoreData, "", false)
|
||||
i++
|
||||
}
|
||||
case 1: // Max RdP overall MP
|
||||
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_mp DESC", rengokuScoreQuery))
|
||||
for rows.Next() {
|
||||
rows.StructScan(&score)
|
||||
if score.Name == s.Name {
|
||||
bf.WriteUint32(i)
|
||||
bf.WriteUint32(score.MaxPointsMP)
|
||||
ps.Uint8(bf, s.Name, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
}
|
||||
scoreData.WriteUint32(i)
|
||||
scoreData.WriteUint32(score.MaxPointsMP)
|
||||
ps.Uint8(scoreData, score.Name, true)
|
||||
ps.Uint8(scoreData, "", false)
|
||||
i++
|
||||
}
|
||||
case 2: // Max stage guild MP
|
||||
if guild != nil {
|
||||
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_mp DESC", rengokuScoreQuery), guild.ID)
|
||||
for rows.Next() {
|
||||
rows.StructScan(&score)
|
||||
if score.Name == s.Name {
|
||||
bf.WriteUint32(i)
|
||||
bf.WriteUint32(score.MaxStagesMP)
|
||||
ps.Uint8(bf, s.Name, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
}
|
||||
scoreData.WriteUint32(i)
|
||||
scoreData.WriteUint32(score.MaxStagesMP)
|
||||
ps.Uint8(scoreData, score.Name, true)
|
||||
ps.Uint8(scoreData, "", false)
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
bf.WriteBytes(make([]byte, 11))
|
||||
}
|
||||
case 3: // Max RdP guild MP
|
||||
if guild != nil {
|
||||
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_mp DESC", rengokuScoreQuery), guild.ID)
|
||||
for rows.Next() {
|
||||
rows.StructScan(&score)
|
||||
if score.Name == s.Name {
|
||||
bf.WriteUint32(i)
|
||||
bf.WriteUint32(score.MaxPointsMP)
|
||||
ps.Uint8(bf, s.Name, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
}
|
||||
scoreData.WriteUint32(i)
|
||||
scoreData.WriteUint32(score.MaxPointsMP)
|
||||
ps.Uint8(scoreData, score.Name, true)
|
||||
ps.Uint8(scoreData, "", false)
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
bf.WriteBytes(make([]byte, 11))
|
||||
}
|
||||
case 4: // Max stage overall SP
|
||||
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_stages_sp DESC", rengokuScoreQuery))
|
||||
for rows.Next() {
|
||||
rows.StructScan(&score)
|
||||
if score.Name == s.Name {
|
||||
bf.WriteUint32(i)
|
||||
bf.WriteUint32(score.MaxStagesSP)
|
||||
ps.Uint8(bf, s.Name, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
}
|
||||
scoreData.WriteUint32(i)
|
||||
scoreData.WriteUint32(score.MaxStagesSP)
|
||||
ps.Uint8(scoreData, score.Name, true)
|
||||
ps.Uint8(scoreData, "", false)
|
||||
i++
|
||||
}
|
||||
case 5: // Max RdP overall SP
|
||||
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s ORDER BY max_points_sp DESC", rengokuScoreQuery))
|
||||
for rows.Next() {
|
||||
rows.StructScan(&score)
|
||||
if score.Name == s.Name {
|
||||
bf.WriteUint32(i)
|
||||
bf.WriteUint32(score.MaxPointsSP)
|
||||
ps.Uint8(bf, s.Name, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
}
|
||||
scoreData.WriteUint32(i)
|
||||
scoreData.WriteUint32(score.MaxPointsSP)
|
||||
ps.Uint8(scoreData, score.Name, true)
|
||||
ps.Uint8(scoreData, "", false)
|
||||
i++
|
||||
}
|
||||
case 6: // Max stage guild SP
|
||||
if guild != nil {
|
||||
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_stages_sp DESC", rengokuScoreQuery), guild.ID)
|
||||
for rows.Next() {
|
||||
rows.StructScan(&score)
|
||||
if score.Name == s.Name {
|
||||
bf.WriteUint32(i)
|
||||
bf.WriteUint32(score.MaxStagesSP)
|
||||
ps.Uint8(bf, s.Name, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
}
|
||||
scoreData.WriteUint32(i)
|
||||
scoreData.WriteUint32(score.MaxStagesSP)
|
||||
ps.Uint8(scoreData, score.Name, true)
|
||||
ps.Uint8(scoreData, "", false)
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
bf.WriteBytes(make([]byte, 11))
|
||||
}
|
||||
case 7: // Max RdP guild SP
|
||||
if guild != nil {
|
||||
rows, _ := s.server.db.Queryx(fmt.Sprintf("%s WHERE guild_id=$1 ORDER BY max_points_sp DESC", rengokuScoreQuery), guild.ID)
|
||||
for rows.Next() {
|
||||
rows.StructScan(&score)
|
||||
if score.Name == s.Name {
|
||||
bf.WriteUint32(i)
|
||||
bf.WriteUint32(score.MaxPointsSP)
|
||||
ps.Uint8(bf, s.Name, true)
|
||||
ps.Uint8(bf, "", false)
|
||||
}
|
||||
scoreData.WriteUint32(i)
|
||||
scoreData.WriteUint32(score.MaxPointsSP)
|
||||
ps.Uint8(scoreData, score.Name, true)
|
||||
ps.Uint8(scoreData, "", false)
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
bf.WriteBytes(make([]byte, 11))
|
||||
}
|
||||
}
|
||||
bf.WriteUint8(uint8(i) - 1)
|
||||
bf.WriteBytes(scoreData.Data())
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfGetRengokuRankingRank(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetRengokuRankingRank)
|
||||
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
// What is this for?
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0) // Max stage overall MP rank
|
||||
bf.WriteUint32(0) // Max RdP overall MP rank
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
@@ -45,12 +45,10 @@ func destructEmptySemaphores(s *Session) {
|
||||
}
|
||||
|
||||
func releaseRaviSemaphore(s *Session, sema *Semaphore) {
|
||||
if !strings.HasSuffix(sema.id_semaphore, "5") {
|
||||
delete(sema.reservedClientSlots, s.charID)
|
||||
delete(sema.clients, s)
|
||||
}
|
||||
if len(sema.reservedClientSlots) == 0 && len(sema.clients) == 0 {
|
||||
s.logger.Debug("Raviente semaphore is empty, resetting")
|
||||
delete(sema.reservedClientSlots, s.charID)
|
||||
delete(sema.clients, s)
|
||||
if strings.HasSuffix(sema.id_semaphore, "2") && len(sema.clients) == 0 {
|
||||
s.logger.Debug("Main raviente semaphore is empty, resetting")
|
||||
resetRavi(s)
|
||||
}
|
||||
}
|
||||
@@ -91,7 +89,7 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
|
||||
suffix, _ := strconv.ParseUint(pkt.SemaphoreID[len(pkt.SemaphoreID)-1:], 10, 32)
|
||||
s.server.semaphore[SemaphoreID] = &Semaphore{
|
||||
id_semaphore: pkt.SemaphoreID,
|
||||
id: uint32(suffix),
|
||||
id: uint32(suffix + 1),
|
||||
clients: make(map[*Session]uint32),
|
||||
reservedClientSlots: make(map[uint32]interface{}),
|
||||
maxPlayers: 32,
|
||||
|
||||
@@ -135,7 +135,6 @@ func removeSessionFromStage(s *Session) {
|
||||
// Remove client from old stage.
|
||||
s.stage.Lock()
|
||||
delete(s.stage.clients, s)
|
||||
delete(s.stage.reservedClientSlots, s.charID)
|
||||
|
||||
// Delete old stage objects owned by the client.
|
||||
s.logger.Info("Sending notification to old stage clients")
|
||||
@@ -157,6 +156,7 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
if s.stageID == "" {
|
||||
s.stageMoveStack.Set(pkt.StageID)
|
||||
} else {
|
||||
s.stage.reservedClientSlots[s.charID] = false
|
||||
s.stageMoveStack.Push(s.stageID)
|
||||
s.stageMoveStack.Lock()
|
||||
}
|
||||
@@ -175,11 +175,18 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
|
||||
// Transfer back to the saved stage ID before the previous move or enter.
|
||||
s.stageMoveStack.Unlock()
|
||||
backStage, err := s.stageMoveStack.Pop()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if _, exists := s.stage.reservedClientSlots[s.charID]; exists {
|
||||
delete(s.stage.reservedClientSlots, s.charID)
|
||||
}
|
||||
|
||||
if _, exists := s.server.stages[backStage].reservedClientSlots[s.charID]; exists {
|
||||
delete(s.server.stages[backStage].reservedClientSlots, s.charID)
|
||||
}
|
||||
|
||||
doStageTransfer(s, pkt.AckHandle, backStage)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package channelserver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
|
||||
"erupe-ce/network/mhfpacket"
|
||||
)
|
||||
|
||||
@@ -58,11 +57,3 @@ func handleMsgMhfGetUdTacticsRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfSetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetUdTacticsLog(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetCafeDurationBonusInfo(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
@@ -15,23 +15,6 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type StageIdType = string
|
||||
|
||||
const (
|
||||
// GlobalStage is the stage that is used for all users.
|
||||
MezeportaStageId StageIdType = "sl1Ns200p0a0u0"
|
||||
GuildHallLv1StageId StageIdType = "sl1Ns202p0a0u0"
|
||||
GuildHallLv2StageId StageIdType = "sl1Ns203p0a0u0"
|
||||
GuildHallLv3StageId StageIdType = "sl1Ns204p0a0u0"
|
||||
PugiFarmStageId StageIdType = "sl1Ns205p0a0u0"
|
||||
RastaBarStageId StageIdType = "sl1Ns211p0a0u0"
|
||||
PalloneCaravanStageId StageIdType = "sl1Ns260p0a0u0"
|
||||
GookFarmStageId StageIdType = "sl1Ns265p0a0u0"
|
||||
DivaFountainStageId StageIdType = "sl2Ns379p0a0u0"
|
||||
DivaHallStageId StageIdType = "sl1Ns445p0a0u0"
|
||||
MezFesStageId StageIdType = "sl1Ns462p0a0u0"
|
||||
)
|
||||
|
||||
// Config struct allows configuring the server.
|
||||
type Config struct {
|
||||
ID uint16
|
||||
@@ -80,8 +63,7 @@ type Server struct {
|
||||
// Discord chat integration
|
||||
discordBot *discordbot.DiscordBot
|
||||
|
||||
name string
|
||||
enable bool
|
||||
name string
|
||||
|
||||
raviente *Raviente
|
||||
}
|
||||
@@ -154,43 +136,30 @@ func NewServer(config *Config) *Server {
|
||||
stages: make(map[string]*Stage),
|
||||
userBinaryParts: make(map[userBinaryPartID][]byte),
|
||||
semaphore: make(map[string]*Semaphore),
|
||||
semaphoreIndex: 5,
|
||||
semaphoreIndex: 7,
|
||||
discordBot: config.DiscordBot,
|
||||
name: config.Name,
|
||||
enable: config.Enable,
|
||||
raviente: NewRaviente(),
|
||||
}
|
||||
|
||||
// Mezeporta
|
||||
s.stages["sl1Ns200p0a0u0"] = NewStage("sl1Ns200p0a0u0")
|
||||
|
||||
// Guild Hall LV1
|
||||
s.stages["sl1Ns202p0a0u0"] = NewStage("sl1Ns202p0a0u0")
|
||||
|
||||
// Guild Hall LV2
|
||||
s.stages["sl1Ns203p0a0u0"] = NewStage("sl1Ns203p0a0u0")
|
||||
|
||||
// Guild Hall LV3
|
||||
s.stages["sl1Ns204p0a0u0"] = NewStage("sl1Ns204p0a0u0")
|
||||
|
||||
// Pugi Farm
|
||||
s.stages["sl1Ns205p0a0u0"] = NewStage("sl1Ns205p0a0u0")
|
||||
|
||||
// Rasta bar stage
|
||||
s.stages["sl1Ns211p0a0u0"] = NewStage("sl1Ns211p0a0u0")
|
||||
|
||||
// Pallone Carvan
|
||||
s.stages["sl1Ns260p0a0u0"] = NewStage("sl1Ns260p0a0u0")
|
||||
|
||||
// Gook Farm
|
||||
s.stages["sl1Ns265p0a0u0"] = NewStage("sl1Ns265p0a0u0")
|
||||
// Pallone Guest House 1st Floor
|
||||
s.stages["sl1Ns262p0a0u0"] = NewStage("sl1Ns262p0a0u0")
|
||||
|
||||
// Pallone Guest House 2nd Floor
|
||||
s.stages["sl1Ns263p0a0u0"] = NewStage("sl1Ns263p0a0u0")
|
||||
|
||||
// Diva fountain / prayer fountain.
|
||||
s.stages["sl2Ns379p0a0u0"] = NewStage("sl2Ns379p0a0u0")
|
||||
|
||||
// Diva Hall
|
||||
s.stages["sl1Ns445p0a0u0"] = NewStage("sl1Ns445p0a0u0")
|
||||
|
||||
// MezFes
|
||||
s.stages["sl1Ns462p0a0u0"] = NewStage("sl1Ns462p0a0u0")
|
||||
|
||||
@@ -367,7 +336,7 @@ func (s *Server) BroadcastRaviente(ip uint32, port uint16, stage []byte, _type u
|
||||
|
||||
func (s *Server) DiscordChannelSend(charName string, content string) {
|
||||
if s.erupeConfig.Discord.Enabled && s.discordBot != nil {
|
||||
message := fmt.Sprintf("**%s** : %s", charName, content)
|
||||
message := fmt.Sprintf("**%s**: %s", charName, content)
|
||||
s.discordBot.RealtimeChannelSend(message)
|
||||
}
|
||||
}
|
||||
@@ -413,6 +382,9 @@ func (s *Server) NextSemaphoreID() uint32 {
|
||||
for {
|
||||
exists := false
|
||||
s.semaphoreIndex = s.semaphoreIndex + 1
|
||||
if s.semaphoreIndex == 0 {
|
||||
s.semaphoreIndex = 7 // Skip reserved indexes
|
||||
}
|
||||
for _, semaphore := range s.semaphore {
|
||||
if semaphore.id == s.semaphoreIndex {
|
||||
exists = true
|
||||
|
||||
@@ -98,35 +98,6 @@ func (s *Stage) isQuest() bool {
|
||||
return len(s.reservedClientSlots) > 0
|
||||
}
|
||||
|
||||
func (s *Stage) GetName() string {
|
||||
switch s.id {
|
||||
case MezeportaStageId:
|
||||
return "Mezeporta"
|
||||
case GuildHallLv1StageId:
|
||||
return "Guild Hall Lv1"
|
||||
case GuildHallLv2StageId:
|
||||
return "Guild Hall Lv2"
|
||||
case GuildHallLv3StageId:
|
||||
return "Guild Hall Lv3"
|
||||
case PugiFarmStageId:
|
||||
return "Pugi Farm"
|
||||
case RastaBarStageId:
|
||||
return "Rasta Bar"
|
||||
case PalloneCaravanStageId:
|
||||
return "Pallone Caravan"
|
||||
case GookFarmStageId:
|
||||
return "Gook Farm"
|
||||
case DivaFountainStageId:
|
||||
return "Diva Fountain"
|
||||
case DivaHallStageId:
|
||||
return "Diva Hall"
|
||||
case MezFesStageId:
|
||||
return "Mez Fes"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stage) NextObjectID() uint32 {
|
||||
s.objectIndex = s.objectIndex + 1
|
||||
// Objects beyond 127 do not duplicate correctly
|
||||
|
||||
@@ -31,6 +31,16 @@ func Time_Current_Midnight() time.Time {
|
||||
return time.Date(baseTime.Year(), baseTime.Month(), baseTime.Day(), 0, 0, 0, 0, baseTime.Location())
|
||||
}
|
||||
|
||||
func TimeWeekStart() time.Time {
|
||||
midnight := Time_Current_Midnight()
|
||||
offset := (int(midnight.Weekday()) - 1) * -24
|
||||
return midnight.Add(time.Hour * time.Duration(offset))
|
||||
}
|
||||
|
||||
func TimeWeekNext() time.Time {
|
||||
return TimeWeekStart().Add(time.Hour * 24 * 7)
|
||||
}
|
||||
|
||||
func Time_Current_Week_uint8() uint8 {
|
||||
baseTime := time.Now().In(time.FixedZone(fmt.Sprintf("UTC+%d", Offset), Offset*60*60)).AddDate(YearAdjust, MonthAdjust, DayAdjust)
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package discordbot
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"erupe-ce/config"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"go.uber.org/zap"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type DiscordBot struct {
|
||||
@@ -16,12 +15,12 @@ type DiscordBot struct {
|
||||
RealtimeChannel *discordgo.Channel
|
||||
}
|
||||
|
||||
type DiscordBotOptions struct {
|
||||
type Options struct {
|
||||
Config *config.Config
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error) {
|
||||
func NewDiscordBot(options Options) (discordBot *DiscordBot, err error) {
|
||||
session, err := discordgo.New("Bot " + options.Config.Discord.BotToken)
|
||||
|
||||
if err != nil {
|
||||
@@ -29,13 +28,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mainGuild, err := session.Guild(options.Config.Discord.ServerID)
|
||||
|
||||
if err != nil {
|
||||
options.Logger.Fatal("Discord failed to get main guild", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
realtimeChannel, err := session.Channel(options.Config.Discord.RealtimeChannelID)
|
||||
|
||||
if err != nil {
|
||||
@@ -47,7 +39,6 @@ func NewDiscordBot(options DiscordBotOptions) (discordBot *DiscordBot, err error
|
||||
config: options.Config,
|
||||
logger: options.Logger,
|
||||
Session: session,
|
||||
MainGuild: mainGuild,
|
||||
RealtimeChannel: realtimeChannel,
|
||||
}
|
||||
|
||||
@@ -60,21 +51,10 @@ func (bot *DiscordBot) Start() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (bot *DiscordBot) FindRoleByID(id string) *discordgo.Role {
|
||||
for _, role := range bot.MainGuild.Roles {
|
||||
if role.ID == id {
|
||||
return role
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Replace all mentions to real name from the message.
|
||||
func (bot *DiscordBot) NormalizeDiscordMessage(message string) string {
|
||||
userRegex := regexp.MustCompile(`<@!?(\d{17,19})>`)
|
||||
emojiRegex := regexp.MustCompile(`(?:<a?)?:(\w+):(?:\d{18}>)?`)
|
||||
roleRegex := regexp.MustCompile(`<@&(\d{17,19})>`)
|
||||
|
||||
result := ReplaceTextAll(message, userRegex, func(userId string) string {
|
||||
user, err := bot.Session.User(userId)
|
||||
@@ -90,17 +70,7 @@ func (bot *DiscordBot) NormalizeDiscordMessage(message string) string {
|
||||
return ":" + emojiName + ":"
|
||||
})
|
||||
|
||||
result = ReplaceTextAll(result, roleRegex, func(roleId string) string {
|
||||
role := bot.FindRoleByID(roleId)
|
||||
|
||||
if role != nil {
|
||||
return "@!" + role.Name
|
||||
}
|
||||
|
||||
return "@!unknown"
|
||||
})
|
||||
|
||||
return string(result)
|
||||
return result
|
||||
}
|
||||
|
||||
func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) {
|
||||
|
||||
Reference in New Issue
Block a user