mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-14 07:55:33 +01:00
Merge remote-tracking branch 'origin/main' into feature/psn-link
# Conflicts: # server/signserver/dbutils.go # server/signserver/dsgn_resp.go # server/signserver/session.go
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"erupe-ce/common/mhfcourse"
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/common/stringsupport"
|
||||
_config "erupe-ce/config"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@@ -27,15 +28,16 @@ func stubEnumerateNoResults(s *Session, ackHandle uint32) {
|
||||
doAckBufSucceed(s, ackHandle, enumBf.Data())
|
||||
}
|
||||
|
||||
// Temporary function to just return no results for many MSG_MHF_GET* packets.
|
||||
func stubGetNoResults(s *Session, ackHandle uint32) {
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(0x0A218EAD) // Unk shared ID. Sent in response of MSG_MHF_GET_TOWER_INFO, MSG_MHF_GET_PAPER_DATA etc. (World ID?)
|
||||
resp.WriteUint32(0) // Unk
|
||||
resp.WriteUint32(0) // Unk
|
||||
resp.WriteUint32(0) // Entry count
|
||||
|
||||
doAckBufSucceed(s, ackHandle, resp.Data())
|
||||
func doAckEarthSucceed(s *Session, ackHandle uint32, data []*byteframe.ByteFrame) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(uint32(s.server.erupeConfig.DevModeOptions.EarthIDOverride))
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint32(uint32(len(data)))
|
||||
for i := range data {
|
||||
bf.WriteBytes(data[i].Data())
|
||||
}
|
||||
doAckBufSucceed(s, ackHandle, bf.Data())
|
||||
}
|
||||
|
||||
func doAckBufSucceed(s *Session, ackHandle uint32, data []byte) {
|
||||
@@ -245,7 +247,7 @@ func logoutPlayer(s *Session) {
|
||||
removeSessionFromStage(s)
|
||||
|
||||
saveData, err := GetCharacterSaveData(s, s.charID)
|
||||
if err != nil {
|
||||
if err != nil || saveData == nil {
|
||||
s.logger.Error("Failed to get savedata")
|
||||
return
|
||||
}
|
||||
@@ -446,48 +448,110 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
case 4: // Find Party
|
||||
type FindPartyParams struct {
|
||||
StagePrefix string
|
||||
RankRestriction uint16
|
||||
Targets []uint16
|
||||
Unk0 []uint16
|
||||
Unk1 []uint16
|
||||
QuestID []uint16
|
||||
}
|
||||
findPartyParams := FindPartyParams{
|
||||
StagePrefix: "sl2Ls210",
|
||||
}
|
||||
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
|
||||
setting := bf.ReadUint8()
|
||||
numParams := int(bf.ReadUint8())
|
||||
maxResults := bf.ReadUint16()
|
||||
bf.Seek(2, 1)
|
||||
partyType := bf.ReadUint16()
|
||||
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()
|
||||
for i := 0; i < numParams; i++ {
|
||||
switch bf.ReadUint8() {
|
||||
case 0:
|
||||
values := int(bf.ReadUint8())
|
||||
for i := 0; i < values; i++ {
|
||||
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
|
||||
findPartyParams.RankRestriction = bf.ReadUint16()
|
||||
} else {
|
||||
findPartyParams.RankRestriction = uint16(bf.ReadInt8())
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
values := int(bf.ReadUint8())
|
||||
for i := 0; i < values; i++ {
|
||||
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
|
||||
findPartyParams.Targets = append(findPartyParams.Targets, bf.ReadUint16())
|
||||
} else {
|
||||
findPartyParams.Targets = append(findPartyParams.Targets, uint16(bf.ReadInt8()))
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
values := int(bf.ReadUint8())
|
||||
for i := 0; i < values; i++ {
|
||||
var value int16
|
||||
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
|
||||
value = bf.ReadInt16()
|
||||
} else {
|
||||
value = int16(bf.ReadInt8())
|
||||
}
|
||||
switch value {
|
||||
case 0: // Public Bar
|
||||
findPartyParams.StagePrefix = "sl2Ls210"
|
||||
case 1: // Tokotoko Partnya
|
||||
findPartyParams.StagePrefix = "sl2Ls463"
|
||||
case 2: // Hunting Prowess Match
|
||||
findPartyParams.StagePrefix = "sl2Ls286"
|
||||
case 3: // Volpakkun Together
|
||||
findPartyParams.StagePrefix = "sl2Ls465"
|
||||
case 5: // Quick Party
|
||||
// Unk
|
||||
}
|
||||
}
|
||||
case 3: // Unknown
|
||||
values := int(bf.ReadUint8())
|
||||
for i := 0; i < values; i++ {
|
||||
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
|
||||
findPartyParams.Unk0 = append(findPartyParams.Unk0, bf.ReadUint16())
|
||||
} else {
|
||||
findPartyParams.Unk0 = append(findPartyParams.Unk0, uint16(bf.ReadInt8()))
|
||||
}
|
||||
}
|
||||
case 4: // Looking for n or already have n
|
||||
values := int(bf.ReadUint8())
|
||||
for i := 0; i < values; i++ {
|
||||
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
|
||||
findPartyParams.Unk1 = append(findPartyParams.Unk1, bf.ReadUint16())
|
||||
} else {
|
||||
findPartyParams.Unk1 = append(findPartyParams.Unk1, uint16(bf.ReadInt8()))
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
values := int(bf.ReadUint8())
|
||||
for i := 0; i < values; i++ {
|
||||
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
|
||||
findPartyParams.QuestID = append(findPartyParams.QuestID, bf.ReadUint16())
|
||||
} else {
|
||||
findPartyParams.QuestID = append(findPartyParams.QuestID, uint16(bf.ReadInt8()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var stagePrefix string
|
||||
switch partyType {
|
||||
case 0: // Public Bar
|
||||
stagePrefix = "sl2Ls210"
|
||||
case 1: // Tokotoko Partnya
|
||||
stagePrefix = "sl2Ls463"
|
||||
case 2: // Hunting Prowess Match
|
||||
stagePrefix = "sl2Ls286"
|
||||
case 3: // Volpakkun Together
|
||||
stagePrefix = "sl2Ls465"
|
||||
case 5: // Quick Party
|
||||
// Unk
|
||||
}
|
||||
for _, c := range s.server.Channels {
|
||||
for _, stage := range c.stages {
|
||||
if count == maxResults {
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(stage.id, stagePrefix) {
|
||||
if strings.HasPrefix(stage.id, findPartyParams.StagePrefix) {
|
||||
sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}])
|
||||
sb3.Seek(4, 0)
|
||||
stageRankRestriction := sb3.ReadUint16()
|
||||
stageTarget := sb3.ReadUint16()
|
||||
if rankRestriction != 0xFFFF && stageRankRestriction < rankRestriction {
|
||||
if stageRankRestriction > findPartyParams.RankRestriction {
|
||||
continue
|
||||
}
|
||||
if len(findPartyParams.Targets) > 0 {
|
||||
for _, target := range findPartyParams.Targets {
|
||||
if target == stageTarget {
|
||||
break
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
count++
|
||||
@@ -551,10 +615,6 @@ func handleMsgSysInfokyserver(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetCaUniqueID(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfEnumerateItem(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfAcquireItem(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfTransferItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfTransferItem)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
@@ -1574,138 +1634,209 @@ func handleMsgMhfStampcardPrize(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfUnreserveSrg(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfReadBeatLevel(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfReadBeatLevel)
|
||||
|
||||
// This response is fixed and will never change on JP,
|
||||
// but I've left it dynamic for possible other client differences.
|
||||
resp := byteframe.NewByteFrame()
|
||||
for i := 0; i < int(pkt.ValidIDCount); i++ {
|
||||
resp.WriteUint32(pkt.IDs[i])
|
||||
resp.WriteUint32(1)
|
||||
resp.WriteUint32(1)
|
||||
resp.WriteUint32(1)
|
||||
}
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfUpdateBeatLevel(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfUpdateBeatLevel)
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfReadBeatLevelAllRanking(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfReadBeatLevelMyRanking(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfReadLastWeekBeatRanking(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetFixedSeibatuRankingTable(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfKickExportForce(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetEarthStatus)
|
||||
|
||||
// TODO(Andoryuuta): Track down format for this data,
|
||||
// it can somehow be parsed as 8*uint32 chunks if the header is right.
|
||||
/*
|
||||
BEFORE ack-refactor:
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(0)
|
||||
resp.WriteUint32(0)
|
||||
|
||||
s.QueueAck(pkt.AckHandle, resp.Data())
|
||||
*/
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{})
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(uint32(TimeWeekStart().Add(time.Hour * -24).Unix())) // Start
|
||||
bf.WriteUint32(uint32(TimeWeekNext().Add(time.Hour * 24).Unix())) // End
|
||||
bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthStatusOverride)
|
||||
bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthIDOverride)
|
||||
bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthMonsterOverride)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfRegistSpabiTime(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetEarthValue(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetEarthValue)
|
||||
var earthValues []struct{ Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32 }
|
||||
if pkt.ReqType == 3 {
|
||||
earthValues = []struct {
|
||||
Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32
|
||||
}{
|
||||
// TW identical to JP
|
||||
{
|
||||
Unk0: 0x03E9,
|
||||
Unk1: 0x24,
|
||||
},
|
||||
{
|
||||
Unk0: 0x2329,
|
||||
Unk1: 0x03,
|
||||
},
|
||||
{
|
||||
Unk0: 0x232A,
|
||||
Unk1: 0x0A,
|
||||
Unk2: 0x012C,
|
||||
},
|
||||
type EarthValues struct {
|
||||
Value []uint32
|
||||
}
|
||||
|
||||
var earthValues []EarthValues
|
||||
switch pkt.ReqType {
|
||||
case 1:
|
||||
earthValues = []EarthValues{
|
||||
{[]uint32{1, 312, 0, 0, 0, 0}},
|
||||
{[]uint32{2, 99, 0, 0, 0, 0}},
|
||||
}
|
||||
} else if pkt.ReqType == 2 {
|
||||
earthValues = []struct {
|
||||
Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32
|
||||
}{
|
||||
// JP response was empty
|
||||
{
|
||||
Unk0: 0x01,
|
||||
Unk1: 0x168B,
|
||||
},
|
||||
{
|
||||
Unk0: 0x02,
|
||||
Unk1: 0x0737,
|
||||
},
|
||||
case 2:
|
||||
earthValues = []EarthValues{
|
||||
{[]uint32{1, 5771, 0, 0, 0, 0}},
|
||||
{[]uint32{2, 1847, 0, 0, 0, 0}},
|
||||
}
|
||||
} else if pkt.ReqType == 1 {
|
||||
earthValues = []struct {
|
||||
Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32
|
||||
}{
|
||||
// JP simply sent 01 and 02 respectively
|
||||
{
|
||||
Unk0: 0x01,
|
||||
Unk1: 0x0138,
|
||||
},
|
||||
{
|
||||
Unk0: 0x02,
|
||||
Unk1: 0x63,
|
||||
},
|
||||
case 3:
|
||||
earthValues = []EarthValues{
|
||||
{[]uint32{1001, 36, 0, 0, 0, 0}},
|
||||
{[]uint32{9001, 3, 0, 0, 0, 0}},
|
||||
{[]uint32{9002, 10, 300, 0, 0, 0}},
|
||||
}
|
||||
}
|
||||
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint32(0x0A218EAD) // Unk shared ID. Sent in response of MSG_MHF_GET_TOWER_INFO, MSG_MHF_GET_PAPER_DATA etc.
|
||||
resp.WriteUint32(0) // Unk
|
||||
resp.WriteUint32(0) // Unk
|
||||
resp.WriteUint32(uint32(len(earthValues))) // value count
|
||||
for _, v := range earthValues {
|
||||
resp.WriteUint32(v.Unk0)
|
||||
resp.WriteUint32(v.Unk1)
|
||||
resp.WriteUint32(v.Unk2)
|
||||
resp.WriteUint32(v.Unk3)
|
||||
resp.WriteUint32(v.Unk4)
|
||||
resp.WriteUint32(v.Unk5)
|
||||
var data []*byteframe.ByteFrame
|
||||
for _, i := range earthValues {
|
||||
bf := byteframe.NewByteFrame()
|
||||
for _, j := range i.Value {
|
||||
bf.WriteUint32(j)
|
||||
}
|
||||
data = append(data, bf)
|
||||
}
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
doAckEarthSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfDebugPostValue(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetNotice(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfPostNotice(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetRandFromTable(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgMhfGetSenyuDailyCount(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfGetSenyuDailyCount(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetSenyuDailyCount)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(0)
|
||||
bf.WriteUint16(0)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
type SeibattleTimetable struct {
|
||||
Start time.Time
|
||||
End time.Time
|
||||
}
|
||||
|
||||
type SeibattleKeyScore struct {
|
||||
Unk0 uint8
|
||||
Unk1 int32
|
||||
}
|
||||
|
||||
type SeibattleCareer struct {
|
||||
Unk0 uint16
|
||||
Unk1 uint16
|
||||
Unk2 uint16
|
||||
}
|
||||
|
||||
type SeibattleOpponent struct {
|
||||
Unk0 int32
|
||||
Unk1 int8
|
||||
}
|
||||
|
||||
type SeibattleConventionResult struct {
|
||||
Unk0 uint32
|
||||
Unk1 uint16
|
||||
Unk2 uint16
|
||||
Unk3 uint16
|
||||
Unk4 uint16
|
||||
}
|
||||
|
||||
type SeibattleCharScore struct {
|
||||
Unk0 uint32
|
||||
}
|
||||
|
||||
type SeibattleCurResult struct {
|
||||
Unk0 uint32
|
||||
Unk1 uint16
|
||||
Unk2 uint16
|
||||
Unk3 uint16
|
||||
}
|
||||
|
||||
type Seibattle struct {
|
||||
Timetable []SeibattleTimetable
|
||||
KeyScore []SeibattleKeyScore
|
||||
Career []SeibattleCareer
|
||||
Opponent []SeibattleOpponent
|
||||
ConventionResult []SeibattleConventionResult
|
||||
CharScore []SeibattleCharScore
|
||||
CurResult []SeibattleCurResult
|
||||
}
|
||||
|
||||
func handleMsgMhfGetSeibattle(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetSeibattle)
|
||||
stubGetNoResults(s, pkt.AckHandle)
|
||||
var data []*byteframe.ByteFrame
|
||||
seibattle := Seibattle{
|
||||
Timetable: []SeibattleTimetable{
|
||||
{TimeMidnight(), TimeMidnight().Add(time.Hour * 8)},
|
||||
{TimeMidnight().Add(time.Hour * 8), TimeMidnight().Add(time.Hour * 16)},
|
||||
{TimeMidnight().Add(time.Hour * 16), TimeMidnight().Add(time.Hour * 24)},
|
||||
},
|
||||
KeyScore: []SeibattleKeyScore{
|
||||
{0, 0},
|
||||
},
|
||||
Career: []SeibattleCareer{
|
||||
{0, 0, 0},
|
||||
},
|
||||
Opponent: []SeibattleOpponent{
|
||||
{1, 1},
|
||||
},
|
||||
ConventionResult: []SeibattleConventionResult{
|
||||
{0, 0, 0, 0, 0},
|
||||
},
|
||||
CharScore: []SeibattleCharScore{
|
||||
{0},
|
||||
},
|
||||
CurResult: []SeibattleCurResult{
|
||||
{0, 0, 0, 0},
|
||||
},
|
||||
}
|
||||
|
||||
switch pkt.Type {
|
||||
case 1:
|
||||
for _, timetable := range seibattle.Timetable {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(uint32(timetable.Start.Unix()))
|
||||
bf.WriteUint32(uint32(timetable.End.Unix()))
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 3: // Key score?
|
||||
for _, keyScore := range seibattle.KeyScore {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(keyScore.Unk0)
|
||||
bf.WriteInt32(keyScore.Unk1)
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 4: // Career?
|
||||
for _, career := range seibattle.Career {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(career.Unk0)
|
||||
bf.WriteUint16(career.Unk1)
|
||||
bf.WriteUint16(career.Unk2)
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 5: // Opponent?
|
||||
for _, opponent := range seibattle.Opponent {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteInt32(opponent.Unk0)
|
||||
bf.WriteInt8(opponent.Unk1)
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 6: // Convention result?
|
||||
for _, conventionResult := range seibattle.ConventionResult {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(conventionResult.Unk0)
|
||||
bf.WriteUint16(conventionResult.Unk1)
|
||||
bf.WriteUint16(conventionResult.Unk2)
|
||||
bf.WriteUint16(conventionResult.Unk3)
|
||||
bf.WriteUint16(conventionResult.Unk4)
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 7: // Char score?
|
||||
for _, charScore := range seibattle.CharScore {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(charScore.Unk0)
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 8: // Cur result?
|
||||
for _, curResult := range seibattle.CurResult {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(curResult.Unk0)
|
||||
bf.WriteUint16(curResult.Unk1)
|
||||
bf.WriteUint16(curResult.Unk2)
|
||||
bf.WriteUint16(curResult.Unk3)
|
||||
data = append(data, bf)
|
||||
}
|
||||
}
|
||||
doAckEarthSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfPostSeibattle(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
@@ -270,6 +270,12 @@ func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostBoostTime)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostBoostTimeLimit)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
@@ -1,18 +1,173 @@
|
||||
package channelserver
|
||||
|
||||
import "erupe-ce/network/mhfpacket"
|
||||
import (
|
||||
"erupe-ce/common/byteframe"
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/common/stringsupport"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CampaignEvent struct {
|
||||
ID uint32
|
||||
Unk0 uint32
|
||||
MinHR int16
|
||||
MaxHR int16
|
||||
MinSR int16
|
||||
MaxSR int16
|
||||
MinGR int16
|
||||
MaxGR int16
|
||||
Unk1 uint16
|
||||
Unk2 uint8
|
||||
Unk3 uint8
|
||||
Unk4 uint16
|
||||
Unk5 uint16
|
||||
Start time.Time
|
||||
End time.Time
|
||||
Unk6 uint8
|
||||
String0 string
|
||||
String1 string
|
||||
String2 string
|
||||
String3 string
|
||||
Link string
|
||||
Prefix string
|
||||
Categories []uint16
|
||||
}
|
||||
|
||||
type CampaignCategory struct {
|
||||
ID uint16
|
||||
Type uint8
|
||||
Title string
|
||||
Description string
|
||||
}
|
||||
|
||||
type CampaignLink struct {
|
||||
CategoryID uint16
|
||||
CampaignID uint32
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateCampaign)
|
||||
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
events := []CampaignEvent{}
|
||||
categories := []CampaignCategory{}
|
||||
var campaignLinks []CampaignLink
|
||||
|
||||
if len(events) > 255 {
|
||||
bf.WriteUint8(255)
|
||||
bf.WriteUint16(uint16(len(events)))
|
||||
} else {
|
||||
bf.WriteUint8(uint8(len(events)))
|
||||
}
|
||||
for _, event := range events {
|
||||
bf.WriteUint32(event.ID)
|
||||
bf.WriteUint32(event.Unk0)
|
||||
bf.WriteInt16(event.MinHR)
|
||||
bf.WriteInt16(event.MaxHR)
|
||||
bf.WriteInt16(event.MinSR)
|
||||
bf.WriteInt16(event.MaxSR)
|
||||
bf.WriteInt16(event.MinGR)
|
||||
bf.WriteInt16(event.MaxGR)
|
||||
bf.WriteUint16(event.Unk1)
|
||||
bf.WriteUint8(event.Unk2)
|
||||
bf.WriteUint8(event.Unk3)
|
||||
bf.WriteUint16(event.Unk4)
|
||||
bf.WriteUint16(event.Unk5)
|
||||
bf.WriteUint32(uint32(event.Start.Unix()))
|
||||
bf.WriteUint32(uint32(event.End.Unix()))
|
||||
bf.WriteUint8(event.Unk6)
|
||||
ps.Uint8(bf, event.String0, true)
|
||||
ps.Uint8(bf, event.String1, true)
|
||||
ps.Uint8(bf, event.String2, true)
|
||||
ps.Uint8(bf, event.String3, true)
|
||||
ps.Uint8(bf, event.Link, true)
|
||||
for i := range event.Categories {
|
||||
campaignLinks = append(campaignLinks, CampaignLink{event.Categories[i], event.ID})
|
||||
}
|
||||
}
|
||||
|
||||
if len(events) > 255 {
|
||||
bf.WriteUint8(255)
|
||||
bf.WriteUint16(uint16(len(events)))
|
||||
} else {
|
||||
bf.WriteUint8(uint8(len(events)))
|
||||
}
|
||||
for _, event := range events {
|
||||
bf.WriteUint32(event.ID)
|
||||
bf.WriteUint8(1) // Always 1?
|
||||
bf.WriteBytes([]byte(event.Prefix))
|
||||
}
|
||||
|
||||
if len(categories) > 255 {
|
||||
bf.WriteUint8(255)
|
||||
bf.WriteUint16(uint16(len(categories)))
|
||||
} else {
|
||||
bf.WriteUint8(uint8(len(categories)))
|
||||
}
|
||||
for _, category := range categories {
|
||||
bf.WriteUint16(category.ID)
|
||||
bf.WriteUint8(category.Type)
|
||||
xTitle := stringsupport.UTF8ToSJIS(category.Title)
|
||||
xDescription := stringsupport.UTF8ToSJIS(category.Description)
|
||||
bf.WriteUint8(uint8(len(xTitle)))
|
||||
bf.WriteUint8(uint8(len(xDescription)))
|
||||
bf.WriteBytes(xTitle)
|
||||
bf.WriteBytes(xDescription)
|
||||
}
|
||||
|
||||
if len(campaignLinks) > 255 {
|
||||
bf.WriteUint8(255)
|
||||
bf.WriteUint16(uint16(len(campaignLinks)))
|
||||
} else {
|
||||
bf.WriteUint8(uint8(len(campaignLinks)))
|
||||
}
|
||||
for _, link := range campaignLinks {
|
||||
bf.WriteUint16(link.CategoryID)
|
||||
bf.WriteUint32(link.CampaignID)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfStateCampaign(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfStateCampaign)
|
||||
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(1)
|
||||
bf.WriteUint16(0)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfApplyCampaign(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfApplyCampaign)
|
||||
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateItem)
|
||||
items := []struct {
|
||||
Unk0 uint32
|
||||
Unk1 uint16
|
||||
Unk2 uint16
|
||||
Unk3 uint16
|
||||
Unk4 uint32
|
||||
Unk5 uint32
|
||||
}{}
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(uint16(len(items)))
|
||||
for _, item := range items {
|
||||
bf.WriteUint32(item.Unk0)
|
||||
bf.WriteUint16(item.Unk1)
|
||||
bf.WriteUint16(item.Unk2)
|
||||
bf.WriteUint16(item.Unk3)
|
||||
bf.WriteUint32(item.Unk4)
|
||||
bf.WriteUint32(item.Unk5)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfAcquireItem(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfAcquireItem)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
@@ -1,21 +1,73 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/common/stringsupport"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RyoudamaReward struct {
|
||||
Unk0 uint8
|
||||
Unk1 uint8
|
||||
Unk2 uint16
|
||||
Unk3 uint16
|
||||
Unk4 uint16
|
||||
Unk5 uint16
|
||||
}
|
||||
|
||||
type RyoudamaKeyScore struct {
|
||||
Unk0 uint8
|
||||
Unk1 int32
|
||||
}
|
||||
|
||||
type RyoudamaCharInfo struct {
|
||||
CID uint32
|
||||
Unk0 int32
|
||||
Name string
|
||||
}
|
||||
|
||||
type RyoudamaBoostInfo struct {
|
||||
Start time.Time
|
||||
End time.Time
|
||||
}
|
||||
|
||||
type Ryoudama struct {
|
||||
Reward []RyoudamaReward
|
||||
KeyScore []RyoudamaKeyScore
|
||||
CharInfo []RyoudamaCharInfo
|
||||
BoostInfo []RyoudamaBoostInfo
|
||||
Score []int32
|
||||
}
|
||||
|
||||
func handleMsgMhfGetRyoudama(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetRyoudama)
|
||||
// likely guild related
|
||||
// REQ: 00 04 13 53 8F 18 00
|
||||
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 01 FE 4E
|
||||
// REQ: 00 06 13 53 8F 18 00
|
||||
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00
|
||||
// REQ: 00 05 13 53 8F 18 00
|
||||
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 0E 2A 15 9E CC 00 00 00 01 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 1E 55 B0 2F 00 00 00 01 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 2A 15 9E CC 00 00 00 02 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 03 D5 30 56 00 00 00 02 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 3F 57 76 9F 00 00 00 03 93 56 92 6E 96 B3 97 70 00 00 00 00 00 00 38 D9 0E C4 00 00 00 03 87 64 83 78 83 42 00 00 00 00 00 00 00 00 23 F3 B9 77 00 00 00 04 82 B3 82 CC 82 DC 82 E9 81 99 00 00 00 00 3F 1B 17 9C 00 00 00 04 82 B1 82 A4 82 BD 00 00 00 00 00 00 00 00 00 B9 F9 C0 00 00 00 05 82 CD 82 E9 82 A9 00 00 00 00 00 00 00 00 23 9F 9A EA 00 00 00 05 83 70 83 62 83 4C 83 83 83 49 00 00 00 00 38 D9 0E C4 00 00 00 06 87 64 83 78 83 42 00 00 00 00 00 00 00 00 1E 55 B0 2F 00 00 00 06 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 03 D5 30 56 00 00 00 07 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 02 D3 B8 77 00 00 00 07 6F 77 6C 32 35 32 35 00 00 00 00 00 00 00
|
||||
data, _ := hex.DecodeString("0A218EAD0000000000000000000000010000000000000000")
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
var data []*byteframe.ByteFrame
|
||||
ryoudama := Ryoudama{Score: []int32{0}}
|
||||
switch pkt.Request2 {
|
||||
case 4:
|
||||
for _, score := range ryoudama.Score {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteInt32(score)
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 5:
|
||||
for _, info := range ryoudama.CharInfo {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(info.CID)
|
||||
bf.WriteInt32(info.Unk0)
|
||||
bf.WriteBytes(stringsupport.PaddedString(info.Name, 14, true))
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 6:
|
||||
for _, info := range ryoudama.BoostInfo {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(uint32(info.Start.Unix()))
|
||||
bf.WriteUint32(uint32(info.End.Unix()))
|
||||
data = append(data, bf)
|
||||
}
|
||||
}
|
||||
doAckEarthSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfPostRyoudama(s *Session, p mhfpacket.MHFPacket) {}
|
||||
@@ -31,8 +83,41 @@ func handleMsgMhfPostTinyBin(s *Session, p mhfpacket.MHFPacket) {
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfCaravanMyScore(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfCaravanMyScore(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfCaravanMyScore)
|
||||
var data []*byteframe.ByteFrame
|
||||
/*
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
*/
|
||||
doAckEarthSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfCaravanRanking(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfCaravanRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfCaravanRanking)
|
||||
var data []*byteframe.ByteFrame
|
||||
/* RYOUDAN
|
||||
bf.WriteInt32(1)
|
||||
bf.WriteUint32(2)
|
||||
bf.WriteBytes(stringsupport.PaddedString("Test", 26, true))
|
||||
*/
|
||||
|
||||
func handleMsgMhfCaravanMyRank(s *Session, p mhfpacket.MHFPacket) {}
|
||||
/* PERSONAL
|
||||
bf.WriteInt32(1)
|
||||
bf.WriteBytes(stringsupport.PaddedString("Test", 14, true))
|
||||
*/
|
||||
doAckEarthSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfCaravanMyRank(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfCaravanMyRank)
|
||||
var data []*byteframe.ByteFrame
|
||||
/*
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
*/
|
||||
doAckEarthSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
@@ -35,16 +35,16 @@ const (
|
||||
BroadcastTypeWorld = 0x0a
|
||||
)
|
||||
|
||||
var commands map[string]config.Command
|
||||
var commands map[string]_config.Command
|
||||
|
||||
func init() {
|
||||
commands = make(map[string]config.Command)
|
||||
commands = make(map[string]_config.Command)
|
||||
zapConfig := zap.NewDevelopmentConfig()
|
||||
zapConfig.DisableCaller = true
|
||||
zapLogger, _ := zapConfig.Build()
|
||||
defer zapLogger.Sync()
|
||||
logger := zapLogger.Named("commands")
|
||||
cmds := config.ErupeConfig.Commands
|
||||
cmds := _config.ErupeConfig.Commands
|
||||
for _, cmd := range cmds {
|
||||
commands[cmd.Name] = cmd
|
||||
if cmd.Enabled {
|
||||
@@ -55,7 +55,7 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
func sendDisabledCommandMessage(s *Session, cmd config.Command) {
|
||||
func sendDisabledCommandMessage(s *Session, cmd _config.Command) {
|
||||
sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandDisabled"], cmd.Name))
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ func parseChatCommand(s *Session, command string) {
|
||||
for _, course := range mhfcourse.Courses() {
|
||||
for _, alias := range course.Aliases() {
|
||||
if strings.ToLower(name) == strings.ToLower(alias) {
|
||||
if slices.Contains(s.server.erupeConfig.Courses, config.Course{Name: course.Aliases()[0], Enabled: true}) {
|
||||
if slices.Contains(s.server.erupeConfig.Courses, _config.Course{Name: course.Aliases()[0], Enabled: true}) {
|
||||
var delta, rightsInt uint32
|
||||
if mhfcourse.CourseExists(course.ID, s.courses) {
|
||||
ei := slices.IndexFunc(s.courses, func(c mhfcourse.Course) bool {
|
||||
|
||||
@@ -5,33 +5,36 @@ import (
|
||||
"errors"
|
||||
"erupe-ce/common/bfutil"
|
||||
"erupe-ce/common/stringsupport"
|
||||
_config "erupe-ce/config"
|
||||
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/server/channelserver/compression/nullcomp"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type SavePointer int
|
||||
|
||||
const (
|
||||
pointerGender = 0x51 // +1
|
||||
pointerRP = 0x22D16 // +2
|
||||
pointerHouseTier = 0x1FB6C // +5
|
||||
pointerHouseData = 0x1FE01 // +195
|
||||
pointerBookshelfData = 0x22298 // +5576
|
||||
// Gallery data also exists at 0x21578, is this the contest submission?
|
||||
pointerGalleryData = 0x22320 // +1748
|
||||
pointerToreData = 0x1FCB4 // +240
|
||||
pointerGardenData = 0x22C58 // +68
|
||||
pointerWeaponType = 0x1F715 // +1
|
||||
pointerWeaponID = 0x1F60A // +2
|
||||
pointerHRP = 0x1FDF6 // +2
|
||||
pointerGRP = 0x1FDFC // +4
|
||||
pointerKQF = 0x23D20 // +8
|
||||
pGender = iota // +1
|
||||
pRP // +2
|
||||
pHouseTier // +5
|
||||
pHouseData // +195
|
||||
pBookshelfData // +5576
|
||||
pGalleryData // +1748
|
||||
pToreData // +240
|
||||
pGardenData // +68
|
||||
pWeaponType // +1
|
||||
pWeaponID // +2
|
||||
pHRP // +2
|
||||
pGRP // +4
|
||||
pKQF // +8
|
||||
)
|
||||
|
||||
type CharacterSaveData struct {
|
||||
CharID uint32
|
||||
Name string
|
||||
IsNewCharacter bool
|
||||
Pointers map[SavePointer]int
|
||||
|
||||
Gender bool
|
||||
RP uint16
|
||||
@@ -51,6 +54,39 @@ type CharacterSaveData struct {
|
||||
decompSave []byte
|
||||
}
|
||||
|
||||
func getPointers() map[SavePointer]int {
|
||||
pointers := map[SavePointer]int{pGender: 81}
|
||||
switch _config.ErupeConfig.RealClientMode {
|
||||
case _config.ZZ:
|
||||
pointers[pWeaponID] = 128522
|
||||
pointers[pWeaponType] = 128789
|
||||
pointers[pHouseTier] = 129900
|
||||
pointers[pToreData] = 130228
|
||||
pointers[pHRP] = 130550
|
||||
pointers[pGRP] = 130556
|
||||
pointers[pHouseData] = 130561
|
||||
pointers[pBookshelfData] = 139928
|
||||
pointers[pGalleryData] = 140064
|
||||
pointers[pGardenData] = 142424
|
||||
pointers[pRP] = 142614
|
||||
pointers[pKQF] = 146720
|
||||
case _config.Z2, _config.Z1, _config.G101, _config.G10:
|
||||
pointers[pWeaponID] = 92522
|
||||
pointers[pWeaponType] = 92789
|
||||
pointers[pHouseTier] = 93900
|
||||
pointers[pToreData] = 94228
|
||||
pointers[pHRP] = 94550
|
||||
pointers[pGRP] = 94556
|
||||
pointers[pHouseData] = 94561
|
||||
pointers[pBookshelfData] = 103928
|
||||
pointers[pGalleryData] = 104064
|
||||
pointers[pGardenData] = 106424
|
||||
pointers[pRP] = 106614
|
||||
pointers[pKQF] = 110720
|
||||
}
|
||||
return pointers
|
||||
}
|
||||
|
||||
func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) {
|
||||
result, err := s.server.db.Query("SELECT id, savedata, is_new_character, name FROM characters WHERE id = $1", charID)
|
||||
if err != nil {
|
||||
@@ -64,7 +100,9 @@ func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
saveData := &CharacterSaveData{}
|
||||
saveData := &CharacterSaveData{
|
||||
Pointers: getPointers(),
|
||||
}
|
||||
err = result.Scan(&saveData.CharID, &saveData.compSave, &saveData.IsNewCharacter, &saveData.Name)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to scan savedata", zap.Error(err), zap.Uint32("charID", charID))
|
||||
@@ -95,13 +133,18 @@ func (save *CharacterSaveData) Save(s *Session) {
|
||||
|
||||
save.updateSaveDataWithStruct()
|
||||
|
||||
err := save.Compress()
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to compress savedata", zap.Error(err))
|
||||
return
|
||||
if _config.ErupeConfig.RealClientMode >= _config.G1 {
|
||||
err := save.Compress()
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to compress savedata", zap.Error(err))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Saves were not compressed
|
||||
save.compSave = save.decompSave
|
||||
}
|
||||
|
||||
_, err = s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hrp=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7
|
||||
_, err := s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hrp=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7
|
||||
`, save.compSave, save.HRP, 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))
|
||||
@@ -133,38 +176,42 @@ func (save *CharacterSaveData) Decompress() error {
|
||||
func (save *CharacterSaveData) updateSaveDataWithStruct() {
|
||||
rpBytes := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(rpBytes, save.RP)
|
||||
copy(save.decompSave[pointerRP:pointerRP+2], rpBytes)
|
||||
copy(save.decompSave[pointerKQF:pointerKQF+8], save.KQF)
|
||||
if _config.ErupeConfig.RealClientMode >= _config.G10 {
|
||||
copy(save.decompSave[save.Pointers[pRP]:save.Pointers[pRP]+2], rpBytes)
|
||||
copy(save.decompSave[save.Pointers[pKQF]:save.Pointers[pKQF]+8], save.KQF)
|
||||
}
|
||||
}
|
||||
|
||||
// This will update the save struct with the values stored in the character save
|
||||
func (save *CharacterSaveData) updateStructWithSaveData() {
|
||||
save.Name = stringsupport.SJISToUTF8(bfutil.UpToNull(save.decompSave[88:100]))
|
||||
if save.decompSave[pointerGender] == 1 {
|
||||
if save.decompSave[save.Pointers[pGender]] == 1 {
|
||||
save.Gender = true
|
||||
} else {
|
||||
save.Gender = false
|
||||
}
|
||||
if !save.IsNewCharacter {
|
||||
save.RP = binary.LittleEndian.Uint16(save.decompSave[pointerRP : pointerRP+2])
|
||||
save.HouseTier = save.decompSave[pointerHouseTier : pointerHouseTier+5]
|
||||
save.HouseData = save.decompSave[pointerHouseData : pointerHouseData+195]
|
||||
save.BookshelfData = save.decompSave[pointerBookshelfData : pointerBookshelfData+5576]
|
||||
save.GalleryData = save.decompSave[pointerGalleryData : pointerGalleryData+1748]
|
||||
save.ToreData = save.decompSave[pointerToreData : pointerToreData+240]
|
||||
save.GardenData = save.decompSave[pointerGardenData : pointerGardenData+68]
|
||||
save.WeaponType = save.decompSave[pointerWeaponType]
|
||||
save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[pointerWeaponID : pointerWeaponID+2])
|
||||
save.HRP = binary.LittleEndian.Uint16(save.decompSave[pointerHRP : pointerHRP+2])
|
||||
if save.HRP == uint16(999) {
|
||||
save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[pointerGRP : pointerGRP+4]))
|
||||
if _config.ErupeConfig.RealClientMode >= _config.G10 {
|
||||
save.RP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pRP] : save.Pointers[pRP]+2])
|
||||
save.HouseTier = save.decompSave[save.Pointers[pHouseTier] : save.Pointers[pHouseTier]+5]
|
||||
save.HouseData = save.decompSave[save.Pointers[pHouseData] : save.Pointers[pHouseData]+195]
|
||||
save.BookshelfData = save.decompSave[save.Pointers[pBookshelfData] : save.Pointers[pBookshelfData]+5576]
|
||||
save.GalleryData = save.decompSave[save.Pointers[pGalleryData] : save.Pointers[pGalleryData]+1748]
|
||||
save.ToreData = save.decompSave[save.Pointers[pToreData] : save.Pointers[pToreData]+240]
|
||||
save.GardenData = save.decompSave[save.Pointers[pGardenData] : save.Pointers[pGardenData]+68]
|
||||
save.WeaponType = save.decompSave[save.Pointers[pWeaponType]]
|
||||
save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pWeaponID] : save.Pointers[pWeaponID]+2])
|
||||
save.HRP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHRP] : save.Pointers[pHRP]+2])
|
||||
if save.HRP == uint16(999) {
|
||||
save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[save.Pointers[pGRP] : save.Pointers[pGRP]+4]))
|
||||
}
|
||||
save.KQF = save.decompSave[save.Pointers[pKQF] : save.Pointers[pKQF]+8]
|
||||
}
|
||||
save.KQF = save.decompSave[pointerKQF : pointerKQF+8]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfSexChanger)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,6 +3,7 @@ package channelserver
|
||||
import (
|
||||
"encoding/hex"
|
||||
"erupe-ce/common/stringsupport"
|
||||
_config "erupe-ce/config"
|
||||
"time"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
@@ -71,7 +72,11 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) {
|
||||
var timestamps []uint32
|
||||
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.DivaEvent >= 0 {
|
||||
if s.server.erupeConfig.DevModeOptions.DivaEvent == 0 {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36))
|
||||
if s.server.erupeConfig.RealClientMode <= _config.Z1 {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 32))
|
||||
} else {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36))
|
||||
}
|
||||
return
|
||||
}
|
||||
timestamps = generateDivaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.DivaEvent), true)
|
||||
@@ -79,9 +84,11 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) {
|
||||
timestamps = generateDivaTimestamps(s, start, false)
|
||||
}
|
||||
|
||||
bf.WriteUint32(id)
|
||||
for _, timestamp := range timestamps {
|
||||
bf.WriteUint32(timestamp)
|
||||
if s.server.erupeConfig.RealClientMode <= _config.Z1 {
|
||||
bf.WriteUint32(id)
|
||||
}
|
||||
for i := range timestamps {
|
||||
bf.WriteUint32(timestamps[i])
|
||||
}
|
||||
|
||||
bf.WriteUint16(0x19) // Unk 00011001
|
||||
|
||||
@@ -2,6 +2,7 @@ package channelserver
|
||||
|
||||
import (
|
||||
"erupe-ce/common/token"
|
||||
_config "erupe-ce/config"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
@@ -47,9 +48,41 @@ func handleMsgMhfReleaseEvent(s *Session, p mhfpacket.MHFPacket) {
|
||||
})
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
Unk0 uint16
|
||||
Unk1 uint16
|
||||
Unk2 uint16
|
||||
Unk3 uint16
|
||||
Unk4 uint16
|
||||
Unk5 uint32
|
||||
Unk6 uint32
|
||||
Unk7 []uint16
|
||||
}
|
||||
|
||||
func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEnumerateEvent)
|
||||
stubEnumerateNoResults(s, pkt.AckHandle)
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
events := []Event{}
|
||||
|
||||
bf.WriteUint8(uint8(len(events)))
|
||||
for _, event := range events {
|
||||
bf.WriteUint16(event.Unk0)
|
||||
bf.WriteUint16(event.Unk1)
|
||||
bf.WriteUint16(event.Unk2)
|
||||
bf.WriteUint16(event.Unk3)
|
||||
bf.WriteUint16(event.Unk4)
|
||||
bf.WriteUint32(event.Unk5)
|
||||
bf.WriteUint32(event.Unk6)
|
||||
if event.Unk0 == 2 {
|
||||
bf.WriteUint8(uint8(len(event.Unk7)))
|
||||
for _, u := range event.Unk7 {
|
||||
bf.WriteUint16(u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
type activeFeature struct {
|
||||
@@ -90,14 +123,24 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
|
||||
func generateFeatureWeapons(count int) activeFeature {
|
||||
if count > 14 {
|
||||
count = 14
|
||||
max := 14
|
||||
if _config.ErupeConfig.RealClientMode < _config.ZZ {
|
||||
max = 13
|
||||
}
|
||||
if _config.ErupeConfig.RealClientMode < _config.G10 {
|
||||
max = 12
|
||||
}
|
||||
if _config.ErupeConfig.RealClientMode < _config.GG {
|
||||
max = 11
|
||||
}
|
||||
if count > max {
|
||||
count = max
|
||||
}
|
||||
nums := make([]int, 0)
|
||||
var result int
|
||||
for len(nums) < count {
|
||||
rng := token.RNG()
|
||||
num := rng.Intn(14)
|
||||
num := rng.Intn(max)
|
||||
exist := false
|
||||
for _, v := range nums {
|
||||
if v == num {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"erupe-ce/common/byteframe"
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/common/token"
|
||||
@@ -140,13 +139,26 @@ func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
|
||||
return timestamps
|
||||
}
|
||||
|
||||
type Trial struct {
|
||||
type FestaTrial struct {
|
||||
ID uint32 `db:"id"`
|
||||
Objective uint8 `db:"objective"`
|
||||
Objective uint16 `db:"objective"`
|
||||
GoalID uint32 `db:"goal_id"`
|
||||
TimesReq uint16 `db:"times_req"`
|
||||
Locale uint16 `db:"locale_req"`
|
||||
Reward uint16 `db:"reward"`
|
||||
Monopoly uint16
|
||||
Unk uint16
|
||||
}
|
||||
|
||||
type FestaReward struct {
|
||||
Unk0 uint8
|
||||
Unk1 uint8
|
||||
ItemType uint16
|
||||
Quantity uint16
|
||||
ItemID uint16
|
||||
Unk5 uint16
|
||||
Unk6 uint16
|
||||
Unk7 uint8
|
||||
}
|
||||
|
||||
func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
||||
@@ -190,36 +202,71 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteUint32(blueSouls)
|
||||
bf.WriteUint32(redSouls)
|
||||
|
||||
var trials []FestaTrial
|
||||
var trial FestaTrial
|
||||
rows, _ = s.server.db.Queryx("SELECT * FROM festa_trials")
|
||||
trialData := byteframe.NewByteFrame()
|
||||
var count uint16
|
||||
for rows.Next() {
|
||||
trial := &Trial{}
|
||||
err := rows.StructScan(&trial)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
trialData.WriteUint32(trial.ID)
|
||||
trialData.WriteUint8(0) // Unk
|
||||
trialData.WriteUint8(trial.Objective)
|
||||
trialData.WriteUint32(trial.GoalID)
|
||||
trialData.WriteUint16(trial.TimesReq)
|
||||
trialData.WriteUint16(trial.Locale)
|
||||
trialData.WriteUint16(trial.Reward)
|
||||
trialData.WriteUint8(0xFF) // Unk
|
||||
trialData.WriteUint8(0xFF) // MonopolyState
|
||||
trialData.WriteUint16(0) // Unk
|
||||
trials = append(trials, trial)
|
||||
}
|
||||
bf.WriteUint16(uint16(len(trials)))
|
||||
for _, trial := range trials {
|
||||
bf.WriteUint32(trial.ID)
|
||||
bf.WriteUint16(trial.Objective)
|
||||
bf.WriteUint32(trial.GoalID)
|
||||
bf.WriteUint16(trial.TimesReq)
|
||||
bf.WriteUint16(trial.Locale)
|
||||
bf.WriteUint16(trial.Reward)
|
||||
trial.Monopoly = 0xFFFF // NYI
|
||||
bf.WriteUint16(trial.Monopoly)
|
||||
bf.WriteUint16(trial.Unk)
|
||||
}
|
||||
bf.WriteUint16(count)
|
||||
bf.WriteBytes(trialData.Data())
|
||||
|
||||
// Static bonus rewards
|
||||
rewards, _ := hex.DecodeString("001901000007015E05F000000000000100000703E81B6300000000010100000C03E8000000000000000100000D0000000000000000000100000100000000000000000002000007015E05F000000000000200000703E81B6300000000010200000C03E8000000000000000200000D0000000000000000000200000400000000000000000003000007015E05F000000000000300000703E81B6300000000010300000C03E8000000000000000300000D0000000000000000000300000100000000000000000004000007015E05F000000000000400000703E81B6300000000010400000C03E8000000000000000400000D0000000000000000000400000400000000000000000005000007015E05F000000000000500000703E81B6300000000010500000C03E8000000000000000500000D00000000000000000005000001000000000000000000")
|
||||
bf.WriteBytes(rewards)
|
||||
// The Winner and Loser Armor IDs are missing
|
||||
rewards := []FestaReward{
|
||||
{1, 0, 7, 350, 1520, 0, 0, 0},
|
||||
{1, 0, 7, 1000, 7011, 0, 0, 1},
|
||||
{1, 0, 12, 1000, 0, 0, 0, 0},
|
||||
{1, 0, 13, 0, 0, 0, 0, 0},
|
||||
//{1, 0, 1, 0, 0, 0, 0, 0},
|
||||
{2, 0, 7, 350, 1520, 0, 0, 0},
|
||||
{2, 0, 7, 1000, 7011, 0, 0, 1},
|
||||
{2, 0, 12, 1000, 0, 0, 0, 0},
|
||||
{2, 0, 13, 0, 0, 0, 0, 0},
|
||||
//{2, 0, 4, 0, 0, 0, 0, 0},
|
||||
{3, 0, 7, 350, 1520, 0, 0, 0},
|
||||
{3, 0, 7, 1000, 7011, 0, 0, 1},
|
||||
{3, 0, 12, 1000, 0, 0, 0, 0},
|
||||
{3, 0, 13, 0, 0, 0, 0, 0},
|
||||
//{3, 0, 1, 0, 0, 0, 0, 0},
|
||||
{4, 0, 7, 350, 1520, 0, 0, 0},
|
||||
{4, 0, 7, 1000, 7011, 0, 0, 1},
|
||||
{4, 0, 12, 1000, 0, 0, 0, 0},
|
||||
{4, 0, 13, 0, 0, 0, 0, 0},
|
||||
//{4, 0, 4, 0, 0, 0, 0, 0},
|
||||
{5, 0, 7, 350, 1520, 0, 0, 0},
|
||||
{5, 0, 7, 1000, 7011, 0, 0, 1},
|
||||
{5, 0, 12, 1000, 0, 0, 0, 0},
|
||||
{5, 0, 13, 0, 0, 0, 0, 0},
|
||||
//{5, 0, 1, 0, 0, 0, 0, 0},
|
||||
}
|
||||
bf.WriteUint16(uint16(len(rewards)))
|
||||
for _, reward := range rewards {
|
||||
bf.WriteUint8(reward.Unk0)
|
||||
bf.WriteUint8(reward.Unk1)
|
||||
bf.WriteUint16(reward.ItemType)
|
||||
bf.WriteUint16(reward.Quantity)
|
||||
bf.WriteUint16(reward.ItemID)
|
||||
bf.WriteUint16(reward.Unk5)
|
||||
bf.WriteUint16(reward.Unk6)
|
||||
bf.WriteUint8(reward.Unk7)
|
||||
}
|
||||
|
||||
bf.WriteUint16(0x0001)
|
||||
bf.WriteUint32(0xD4C001F4)
|
||||
bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MaximumFP)
|
||||
bf.WriteUint16(500)
|
||||
|
||||
categoryWinners := uint16(0) // NYI
|
||||
bf.WriteUint16(categoryWinners)
|
||||
@@ -239,8 +286,18 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
|
||||
ps.Uint8(bf, "", true) // Guild Name
|
||||
}
|
||||
|
||||
d, _ := hex.DecodeString("000000000000000100001388000007D0000003E800000064012C00C8009600640032")
|
||||
bf.WriteBytes(d)
|
||||
// Unknown values
|
||||
bf.WriteUint32(1)
|
||||
bf.WriteUint32(5000)
|
||||
bf.WriteUint32(2000)
|
||||
bf.WriteUint32(1000)
|
||||
bf.WriteUint32(100)
|
||||
bf.WriteUint16(300)
|
||||
bf.WriteUint16(200)
|
||||
bf.WriteUint16(150)
|
||||
bf.WriteUint16(100)
|
||||
bf.WriteUint16(50)
|
||||
|
||||
ps.Uint16(bf, "", false)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
_config "erupe-ce/config"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
@@ -62,7 +63,6 @@ type Guild struct {
|
||||
Recruiting bool `db:"recruiting"`
|
||||
FestivalColour FestivalColour `db:"festival_colour"`
|
||||
Souls uint32 `db:"souls"`
|
||||
Rank uint16 `db:"rank"`
|
||||
AllianceID uint32 `db:"alliance_id"`
|
||||
Icon *GuildIcon `db:"icon"`
|
||||
|
||||
@@ -115,6 +115,39 @@ func (gi *GuildIcon) Value() (valuer driver.Value, err error) {
|
||||
return json.Marshal(gi)
|
||||
}
|
||||
|
||||
func (g *Guild) Rank() uint16 {
|
||||
rpMap := []uint32{
|
||||
24, 48, 96, 144, 192, 240, 288, 360, 432,
|
||||
504, 600, 696, 792, 888, 984, 1080, 1200,
|
||||
}
|
||||
if _config.ErupeConfig.RealClientMode <= _config.Z2 {
|
||||
rpMap = []uint32{
|
||||
3500, 6000, 8500, 11000, 13500, 16000, 20000, 24000, 28000,
|
||||
33000, 38000, 43000, 48000, 55000, 70000, 90000, 120000,
|
||||
}
|
||||
}
|
||||
for i, u := range rpMap {
|
||||
if g.RankRP < u {
|
||||
if _config.ErupeConfig.RealClientMode <= _config.S6 && i >= 12 {
|
||||
return 12
|
||||
} else if _config.ErupeConfig.RealClientMode <= _config.F5 && i >= 13 {
|
||||
return 13
|
||||
} else if _config.ErupeConfig.RealClientMode <= _config.G32 && i >= 14 {
|
||||
return 14
|
||||
}
|
||||
return uint16(i)
|
||||
}
|
||||
}
|
||||
if _config.ErupeConfig.RealClientMode <= _config.S6 {
|
||||
return 12
|
||||
} else if _config.ErupeConfig.RealClientMode <= _config.F5 {
|
||||
return 13
|
||||
} else if _config.ErupeConfig.RealClientMode <= _config.G32 {
|
||||
return 14
|
||||
}
|
||||
return 17
|
||||
}
|
||||
|
||||
const guildInfoSelectQuery = `
|
||||
SELECT
|
||||
g.id,
|
||||
@@ -137,14 +170,6 @@ SELECT
|
||||
recruiting,
|
||||
COALESCE((SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id), 'none') AS festival_colour,
|
||||
(SELECT SUM(souls) FROM guild_characters gc WHERE gc.guild_id = g.id) AS souls,
|
||||
CASE
|
||||
WHEN rank_rp <= 48 THEN rank_rp/24
|
||||
WHEN rank_rp <= 288 THEN rank_rp/48+1
|
||||
WHEN rank_rp <= 504 THEN rank_rp/72+3
|
||||
WHEN rank_rp <= 1080 THEN (rank_rp-24)/96+5
|
||||
WHEN rank_rp < 1200 THEN 16
|
||||
ELSE 17
|
||||
END rank,
|
||||
COALESCE((
|
||||
SELECT id FROM guild_alliances ga WHERE
|
||||
ga.parent_id = g.id OR
|
||||
@@ -615,13 +640,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfOperateGuild)
|
||||
|
||||
guild, err := GetGuildInfoByID(s, pkt.GuildID)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
characterGuildInfo, err := GetCharacterGuildData(s, s.charID)
|
||||
|
||||
if err != nil {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
@@ -630,22 +649,19 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
switch pkt.Action {
|
||||
case mhfpacket.OPERATE_GUILD_DISBAND:
|
||||
case mhfpacket.OperateGuildDisband:
|
||||
response := 1
|
||||
if guild.LeaderCharID != s.charID {
|
||||
s.logger.Warn(fmt.Sprintf("character '%d' is attempting to manage guild '%d' without permission", s.charID, guild.ID))
|
||||
return
|
||||
response = 0
|
||||
} else {
|
||||
err = guild.Disband(s)
|
||||
if err != nil {
|
||||
response = 0
|
||||
}
|
||||
}
|
||||
|
||||
err = guild.Disband(s)
|
||||
response := 0x01
|
||||
|
||||
if err != nil {
|
||||
// All successful acks return 0x01, assuming 0x00 is failure
|
||||
response = 0x00
|
||||
}
|
||||
|
||||
bf.WriteUint32(uint32(response))
|
||||
case mhfpacket.OPERATE_GUILD_RESIGN:
|
||||
case mhfpacket.OperateGuildResign:
|
||||
guildMembers, err := GetGuildMembers(s, guild.ID, false)
|
||||
if err == nil {
|
||||
sort.Slice(guildMembers[:], func(i, j int) bool {
|
||||
@@ -664,25 +680,22 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
guild.Save(s)
|
||||
}
|
||||
case mhfpacket.OPERATE_GUILD_APPLY:
|
||||
case mhfpacket.OperateGuildApply:
|
||||
err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil)
|
||||
|
||||
if err == nil {
|
||||
bf.WriteUint32(guild.LeaderCharID)
|
||||
} else {
|
||||
bf.WriteUint32(0)
|
||||
}
|
||||
case mhfpacket.OPERATE_GUILD_LEAVE:
|
||||
var err error
|
||||
|
||||
case mhfpacket.OperateGuildLeave:
|
||||
if characterGuildInfo.IsApplicant {
|
||||
err = guild.RejectApplication(s, s.charID)
|
||||
} else {
|
||||
err = guild.RemoveCharacter(s, s.charID)
|
||||
}
|
||||
|
||||
response := 0x01
|
||||
response := 1
|
||||
if err != nil {
|
||||
// All successful acks return 0x01, assuming 0x00 is failure
|
||||
response = 0x00
|
||||
response = 0
|
||||
} else {
|
||||
mail := Mail{
|
||||
RecipientID: s.charID,
|
||||
@@ -692,26 +705,25 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
mail.Send(s, nil)
|
||||
}
|
||||
|
||||
bf.WriteUint32(uint32(response))
|
||||
case mhfpacket.OPERATE_GUILD_DONATE_RANK:
|
||||
case mhfpacket.OperateGuildDonateRank:
|
||||
bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, false))
|
||||
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_DENY:
|
||||
case mhfpacket.OperateGuildSetApplicationDeny:
|
||||
s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID)
|
||||
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_ALLOW:
|
||||
case mhfpacket.OperateGuildSetApplicationAllow:
|
||||
s.server.db.Exec("UPDATE guilds SET recruiting=true WHERE id=$1", guild.ID)
|
||||
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE:
|
||||
case mhfpacket.OperateGuildSetAvoidLeadershipTrue:
|
||||
handleAvoidLeadershipUpdate(s, pkt, true)
|
||||
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE:
|
||||
case mhfpacket.OperateGuildSetAvoidLeadershipFalse:
|
||||
handleAvoidLeadershipUpdate(s, pkt, false)
|
||||
case mhfpacket.OPERATE_GUILD_UPDATE_COMMENT:
|
||||
case mhfpacket.OperateGuildUpdateComment:
|
||||
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
}
|
||||
guild.Comment = stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())
|
||||
guild.Save(s)
|
||||
case mhfpacket.OPERATE_GUILD_UPDATE_MOTTO:
|
||||
case mhfpacket.OperateGuildUpdateMotto:
|
||||
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
|
||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||
return
|
||||
@@ -720,27 +732,29 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
guild.SubMotto = pkt.Data1.ReadUint8()
|
||||
guild.MainMotto = pkt.Data1.ReadUint8()
|
||||
guild.Save(s)
|
||||
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_1:
|
||||
case mhfpacket.OperateGuildRenamePugi1:
|
||||
handleRenamePugi(s, pkt.Data2, guild, 1)
|
||||
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_2:
|
||||
case mhfpacket.OperateGuildRenamePugi2:
|
||||
handleRenamePugi(s, pkt.Data2, guild, 2)
|
||||
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_3:
|
||||
case mhfpacket.OperateGuildRenamePugi3:
|
||||
handleRenamePugi(s, pkt.Data2, guild, 3)
|
||||
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_1:
|
||||
case mhfpacket.OperateGuildChangePugi1:
|
||||
handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 1)
|
||||
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_2:
|
||||
case mhfpacket.OperateGuildChangePugi2:
|
||||
handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 2)
|
||||
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_3:
|
||||
case mhfpacket.OperateGuildChangePugi3:
|
||||
handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 3)
|
||||
case mhfpacket.OPERATE_GUILD_UNLOCK_OUTFIT:
|
||||
case mhfpacket.OperateGuildUnlockOutfit:
|
||||
// TODO: This doesn't implement blocking, if someone unlocked the same outfit at the same time
|
||||
s.server.db.Exec(`UPDATE guilds SET pugi_outfits=pugi_outfits+$1 WHERE id=$2`, int(math.Pow(float64(pkt.Data1.ReadUint32()), 2)), guild.ID)
|
||||
case mhfpacket.OPERATE_GUILD_DONATE_EVENT:
|
||||
case mhfpacket.OperateGuildDonateRoom:
|
||||
// TODO: Where does this go?
|
||||
case mhfpacket.OperateGuildDonateEvent:
|
||||
quantity := uint16(pkt.Data1.ReadUint32())
|
||||
bf.WriteBytes(handleDonateRP(s, quantity, guild, true))
|
||||
// TODO: Move this value onto rp_yesterday and reset to 0... daily?
|
||||
s.server.db.Exec(`UPDATE guild_characters SET rp_today=rp_today+$1 WHERE character_id=$2`, quantity, s.charID)
|
||||
case mhfpacket.OPERATE_GUILD_EVENT_EXCHANGE:
|
||||
case mhfpacket.OperateGuildEventExchange:
|
||||
rp := uint16(pkt.Data1.ReadUint32())
|
||||
var balance uint32
|
||||
s.server.db.QueryRow(`UPDATE guilds SET event_rp=event_rp-$1 WHERE id=$2 RETURNING event_rp`, rp, guild.ID).Scan(&balance)
|
||||
@@ -922,14 +936,19 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
bf.WriteUint32(guild.ID)
|
||||
bf.WriteUint32(guild.LeaderCharID)
|
||||
bf.WriteUint16(guild.Rank)
|
||||
bf.WriteUint16(guild.Rank())
|
||||
bf.WriteUint16(guild.MemberCount)
|
||||
|
||||
bf.WriteUint8(guild.MainMotto)
|
||||
bf.WriteUint8(guild.SubMotto)
|
||||
|
||||
// Unk appears to be static
|
||||
bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteUint8(0)
|
||||
|
||||
bf.WriteBool(!guild.Recruiting)
|
||||
|
||||
@@ -952,28 +971,39 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteUint8(FestivalColourCodes[guild.FestivalColour])
|
||||
bf.WriteUint32(guild.RankRP)
|
||||
bf.WriteBytes(guildLeaderName)
|
||||
bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00}) // Unk
|
||||
bf.WriteBool(false) // isReturnGuild
|
||||
bf.WriteBool(false) // earnedSpecialHall
|
||||
bf.WriteBytes([]byte{0x02, 0x02}) // Unk
|
||||
bf.WriteUint32(guild.EventRP)
|
||||
bf.WriteUint32(0) // Unk
|
||||
bf.WriteBool(false) // isReturnGuild
|
||||
bf.WriteBool(false) // earnedSpecialHall
|
||||
bf.WriteUint8(2)
|
||||
bf.WriteUint8(2)
|
||||
bf.WriteUint32(guild.EventRP) // Skipped if last byte is <2?
|
||||
ps.Uint8(bf, guild.PugiName1, true)
|
||||
ps.Uint8(bf, guild.PugiName2, true)
|
||||
ps.Uint8(bf, guild.PugiName3, true)
|
||||
bf.WriteUint8(guild.PugiOutfit1)
|
||||
bf.WriteUint8(guild.PugiOutfit2)
|
||||
bf.WriteUint8(guild.PugiOutfit3)
|
||||
bf.WriteUint8(guild.PugiOutfit1)
|
||||
bf.WriteUint8(guild.PugiOutfit2)
|
||||
bf.WriteUint8(guild.PugiOutfit3)
|
||||
if s.server.erupeConfig.RealClientMode >= _config.Z1 {
|
||||
bf.WriteUint8(guild.PugiOutfit1)
|
||||
bf.WriteUint8(guild.PugiOutfit2)
|
||||
bf.WriteUint8(guild.PugiOutfit3)
|
||||
}
|
||||
bf.WriteUint32(guild.PugiOutfits)
|
||||
|
||||
// Unk flags
|
||||
bf.WriteUint8(0x3C) // also seen as 0x32 on JP and 0x64 on TW
|
||||
if guild.Rank() >= 3 {
|
||||
bf.WriteUint8(40)
|
||||
} else if guild.Rank() >= 7 {
|
||||
bf.WriteUint8(50)
|
||||
} else if guild.Rank() >= 10 {
|
||||
bf.WriteUint8(60)
|
||||
} else {
|
||||
bf.WriteUint8(30)
|
||||
}
|
||||
|
||||
bf.WriteBytes([]byte{
|
||||
0x00, 0x00, 0xD6, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
})
|
||||
bf.WriteUint32(55000)
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint16(0) // Changing Room RP
|
||||
bf.WriteUint16(0)
|
||||
|
||||
if guild.AllianceID > 0 {
|
||||
alliance, err := GetAllianceData(s, guild.AllianceID)
|
||||
@@ -983,7 +1013,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteUint32(alliance.ID)
|
||||
bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
|
||||
bf.WriteUint16(alliance.TotalMembers)
|
||||
bf.WriteUint16(0) // Unk0
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteUint8(0)
|
||||
ps.Uint16(bf, alliance.Name, true)
|
||||
if alliance.SubGuild1ID > 0 {
|
||||
if alliance.SubGuild2ID > 0 {
|
||||
@@ -1001,7 +1032,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
} else {
|
||||
bf.WriteUint16(0)
|
||||
}
|
||||
bf.WriteUint16(alliance.ParentGuild.Rank)
|
||||
bf.WriteUint16(alliance.ParentGuild.Rank())
|
||||
bf.WriteUint16(alliance.ParentGuild.MemberCount)
|
||||
ps.Uint16(bf, alliance.ParentGuild.Name, true)
|
||||
ps.Uint16(bf, alliance.ParentGuild.LeaderName, true)
|
||||
@@ -1013,7 +1044,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
} else {
|
||||
bf.WriteUint16(0)
|
||||
}
|
||||
bf.WriteUint16(alliance.SubGuild1.Rank)
|
||||
bf.WriteUint16(alliance.SubGuild1.Rank())
|
||||
bf.WriteUint16(alliance.SubGuild1.MemberCount)
|
||||
ps.Uint16(bf, alliance.SubGuild1.Name, true)
|
||||
ps.Uint16(bf, alliance.SubGuild1.LeaderName, true)
|
||||
@@ -1026,7 +1057,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
} else {
|
||||
bf.WriteUint16(0)
|
||||
}
|
||||
bf.WriteUint16(alliance.SubGuild2.Rank)
|
||||
bf.WriteUint16(alliance.SubGuild2.Rank())
|
||||
bf.WriteUint16(alliance.SubGuild2.MemberCount)
|
||||
ps.Uint16(bf, alliance.SubGuild2.Name, true)
|
||||
ps.Uint16(bf, alliance.SubGuild2.LeaderName, true)
|
||||
@@ -1043,29 +1074,46 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteUint16(uint16(len(applicants)))
|
||||
for _, applicant := range applicants {
|
||||
bf.WriteUint32(applicant.CharID)
|
||||
bf.WriteUint16(0)
|
||||
bf.WriteUint16(0)
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint16(applicant.HRP)
|
||||
bf.WriteUint16(applicant.GR)
|
||||
ps.Uint8(bf, applicant.Name, true)
|
||||
}
|
||||
}
|
||||
|
||||
bf.WriteUint16(0x0000) // lenAllianceApplications
|
||||
type UnkGuildInfo struct {
|
||||
Unk0 uint8
|
||||
Unk1 uint8
|
||||
Unk2 uint8
|
||||
}
|
||||
unkGuildInfo := []UnkGuildInfo{}
|
||||
bf.WriteUint8(uint8(len(unkGuildInfo)))
|
||||
for _, info := range unkGuildInfo {
|
||||
bf.WriteUint8(info.Unk0)
|
||||
bf.WriteUint8(info.Unk1)
|
||||
bf.WriteUint8(info.Unk2)
|
||||
}
|
||||
|
||||
/*
|
||||
alliance application format
|
||||
uint16 numapplicants (above)
|
||||
|
||||
uint32 guild id
|
||||
uint32 guild leader id (for mail)
|
||||
uint32 unk (always null in pcap)
|
||||
uint16 member count
|
||||
uint16 len guild name
|
||||
string nullterm guild name
|
||||
uint16 len guild leader name
|
||||
string nullterm guild leader name
|
||||
*/
|
||||
type AllianceInvite struct {
|
||||
GuildID uint32
|
||||
LeaderID uint32
|
||||
Unk0 uint16
|
||||
Unk1 uint16
|
||||
Members uint16
|
||||
GuildName string
|
||||
LeaderName string
|
||||
}
|
||||
allianceInvites := []AllianceInvite{}
|
||||
bf.WriteUint8(uint8(len(allianceInvites)))
|
||||
for _, invite := range allianceInvites {
|
||||
bf.WriteUint32(invite.GuildID)
|
||||
bf.WriteUint32(invite.LeaderID)
|
||||
bf.WriteUint16(invite.Unk0)
|
||||
bf.WriteUint16(invite.Unk1)
|
||||
bf.WriteUint16(invite.Members)
|
||||
ps.Uint16(bf, invite.GuildName, true)
|
||||
ps.Uint16(bf, invite.LeaderName, true)
|
||||
}
|
||||
|
||||
if guild.Icon != nil {
|
||||
bf.WriteUint8(uint8(len(guild.Icon.Parts)))
|
||||
@@ -1083,7 +1131,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteUint16(p.PosY)
|
||||
}
|
||||
} else {
|
||||
bf.WriteUint8(0x00)
|
||||
bf.WriteUint8(0)
|
||||
}
|
||||
bf.WriteUint8(0) // Unk
|
||||
|
||||
@@ -1285,7 +1333,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||
bf.WriteUint32(guild.LeaderCharID)
|
||||
bf.WriteUint16(guild.MemberCount)
|
||||
bf.WriteUint16(0x0000) // Unk
|
||||
bf.WriteUint16(guild.Rank)
|
||||
bf.WriteUint16(guild.Rank())
|
||||
bf.WriteUint32(uint32(guild.CreatedAt.Unix()))
|
||||
ps.Uint8(bf, guild.Name, true)
|
||||
ps.Uint8(bf, guild.LeaderName, true)
|
||||
@@ -1347,7 +1395,7 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {
|
||||
if guild != nil {
|
||||
isApplicant, _ := guild.HasApplicationForCharID(s, s.charID)
|
||||
if isApplicant {
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2))
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -1389,8 +1437,15 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {
|
||||
for _, member := range guildMembers {
|
||||
bf.WriteUint32(member.CharID)
|
||||
bf.WriteUint16(member.HRP)
|
||||
bf.WriteUint16(member.GR)
|
||||
bf.WriteUint16(member.WeaponID)
|
||||
if s.server.erupeConfig.RealClientMode > _config.G7 {
|
||||
bf.WriteUint16(member.GR)
|
||||
}
|
||||
if s.server.erupeConfig.RealClientMode < _config.ZZ {
|
||||
// Magnet Spike crash workaround
|
||||
bf.WriteUint16(0)
|
||||
} else {
|
||||
bf.WriteUint16(member.WeaponID)
|
||||
}
|
||||
if member.WeaponType == 1 || member.WeaponType == 5 || member.WeaponType == 10 { // If weapon is ranged
|
||||
bf.WriteUint8(7)
|
||||
} else {
|
||||
@@ -1450,7 +1505,6 @@ func handleMsgMhfGetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetGuildManageRight)
|
||||
|
||||
guild, err := GetGuildInfoByCharacterId(s, s.charID)
|
||||
|
||||
if guild == nil && s.prevGuildID != 0 {
|
||||
guild, err = GetGuildInfoByID(s, s.prevGuildID)
|
||||
s.prevGuildID = 0
|
||||
@@ -1460,31 +1514,14 @@ func handleMsgMhfGetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.logger.Warn("failed to respond to manage rights message")
|
||||
return
|
||||
} else if guild == nil {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(0x00) // Unk
|
||||
bf.WriteUint16(0x00) // Member count
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
return
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
bf.WriteUint16(0x00) // Unk
|
||||
bf.WriteUint16(guild.MemberCount)
|
||||
|
||||
bf.WriteUint32(uint32(guild.MemberCount))
|
||||
members, _ := GetGuildMembers(s, guild.ID, false)
|
||||
|
||||
for _, member := range members {
|
||||
bf.WriteUint32(member.CharID)
|
||||
bf.WriteBool(member.Recruiter)
|
||||
bf.WriteBytes(make([]byte, 3))
|
||||
}
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
|
||||
@@ -209,14 +209,14 @@ func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
}
|
||||
bf.WriteUint32(alliance.ParentGuildID)
|
||||
bf.WriteUint32(alliance.ParentGuild.LeaderCharID)
|
||||
bf.WriteUint16(alliance.ParentGuild.Rank)
|
||||
bf.WriteUint16(alliance.ParentGuild.Rank())
|
||||
bf.WriteUint16(alliance.ParentGuild.MemberCount)
|
||||
ps.Uint16(bf, alliance.ParentGuild.Name, true)
|
||||
ps.Uint16(bf, alliance.ParentGuild.LeaderName, true)
|
||||
if alliance.SubGuild1ID > 0 {
|
||||
bf.WriteUint32(alliance.SubGuild1ID)
|
||||
bf.WriteUint32(alliance.SubGuild1.LeaderCharID)
|
||||
bf.WriteUint16(alliance.SubGuild1.Rank)
|
||||
bf.WriteUint16(alliance.SubGuild1.Rank())
|
||||
bf.WriteUint16(alliance.SubGuild1.MemberCount)
|
||||
ps.Uint16(bf, alliance.SubGuild1.Name, true)
|
||||
ps.Uint16(bf, alliance.SubGuild1.LeaderName, true)
|
||||
@@ -224,7 +224,7 @@ func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
|
||||
if alliance.SubGuild2ID > 0 {
|
||||
bf.WriteUint32(alliance.SubGuild2ID)
|
||||
bf.WriteUint32(alliance.SubGuild2.LeaderCharID)
|
||||
bf.WriteUint16(alliance.SubGuild2.Rank)
|
||||
bf.WriteUint16(alliance.SubGuild2.Rank())
|
||||
bf.WriteUint16(alliance.SubGuild2.MemberCount)
|
||||
ps.Uint16(bf, alliance.SubGuild2.Name, true)
|
||||
ps.Uint16(bf, alliance.SubGuild2.LeaderName, true)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"erupe-ce/common/byteframe"
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/common/stringsupport"
|
||||
_config "erupe-ce/config"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
@@ -249,6 +250,9 @@ func handleMsgMhfLoadDecoMyset(s *Session, p mhfpacket.MHFPacket) {
|
||||
s.logger.Error("Failed to load decomyset", zap.Error(err))
|
||||
}
|
||||
if len(data) == 0 {
|
||||
if s.server.erupeConfig.RealClientMode <= _config.G7 {
|
||||
data = []byte{0x00, 0x00}
|
||||
}
|
||||
data = []byte{0x01, 0x00}
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
|
||||
@@ -3,6 +3,7 @@ package channelserver
|
||||
import (
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/common/stringsupport"
|
||||
_config "erupe-ce/config"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/server/channelserver/compression/deltacomp"
|
||||
"erupe-ce/server/channelserver/compression/nullcomp"
|
||||
@@ -56,11 +57,15 @@ func handleMsgMhfLoadLegendDispatch(s *Session, p mhfpacket.MHFPacket) {
|
||||
|
||||
func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfLoadHunterNavi)
|
||||
naviLength := 552
|
||||
if s.server.erupeConfig.RealClientMode <= _config.G7 {
|
||||
naviLength = 280
|
||||
}
|
||||
var data []byte
|
||||
err := s.server.db.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.charID).Scan(&data)
|
||||
if len(data) == 0 {
|
||||
s.logger.Error("Failed to load hunternavi", zap.Error(err))
|
||||
data = make([]byte, 0x226)
|
||||
data = make([]byte, naviLength)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
@@ -68,6 +73,10 @@ func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfSaveHunterNavi)
|
||||
if pkt.IsDataDiff {
|
||||
naviLength := 552
|
||||
if s.server.erupeConfig.RealClientMode <= _config.G7 {
|
||||
naviLength = 280
|
||||
}
|
||||
var data []byte
|
||||
// Load existing save
|
||||
err := s.server.db.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.charID).Scan(&data)
|
||||
@@ -78,7 +87,7 @@ func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
|
||||
// Check if we actually had any hunternavi data, using a blank buffer if not.
|
||||
// This is requried as the client will try to send a diff after character creation without a prior MsgMhfSaveHunterNavi packet.
|
||||
if len(data) == 0 {
|
||||
data = make([]byte, 0x226)
|
||||
data = make([]byte, naviLength)
|
||||
}
|
||||
|
||||
// Perform diff and compress it to write back to db
|
||||
@@ -222,8 +231,8 @@ func handleMsgMhfReadMercenaryM(s *Session, p mhfpacket.MHFPacket) {
|
||||
func handleMsgMhfContractMercenary(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfContractMercenary)
|
||||
switch pkt.Op {
|
||||
case 0:
|
||||
s.server.db.Exec("UPDATE characters SET pact_id=$1 WHERE id=$2", pkt.PactMercID, s.charID)
|
||||
case 0: // Form loan
|
||||
s.server.db.Exec("UPDATE characters SET pact_id=$1 WHERE id=$2", pkt.PactMercID, pkt.CID)
|
||||
case 1: // Cancel lend
|
||||
s.server.db.Exec("UPDATE characters SET pact_id=0 WHERE id=$1", s.charID)
|
||||
case 2: // Cancel loan
|
||||
|
||||
@@ -78,7 +78,8 @@ func handleMsgSysRotateObject(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgSysDuplicateObject(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgSysSetObjectBinary)
|
||||
_ = p.(*mhfpacket.MsgSysSetObjectBinary)
|
||||
/* This causes issues with PS3 as this actually sends with endiness!
|
||||
for _, session := range s.server.sessions {
|
||||
if session.charID == s.charID {
|
||||
s.server.userBinaryPartsLock.Lock()
|
||||
@@ -91,6 +92,7 @@ func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
s.server.BroadcastMHF(msg, s)
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
func handleMsgSysGetObjectBinary(s *Session, p mhfpacket.MHFPacket) {}
|
||||
|
||||
File diff suppressed because one or more lines are too long
106
server/channelserver/handlers_seibattle.go
Normal file
106
server/channelserver/handlers_seibattle.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
)
|
||||
|
||||
func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetBreakSeibatuLevelReward)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
type WeeklySeibatuRankingReward struct {
|
||||
Unk0 int32
|
||||
Unk1 int32
|
||||
Unk2 uint32
|
||||
Unk3 int32
|
||||
Unk4 int32
|
||||
Unk5 int32
|
||||
}
|
||||
|
||||
func handleMsgMhfGetWeeklySeibatuRankingReward(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetWeeklySeibatuRankingReward)
|
||||
var data []*byteframe.ByteFrame
|
||||
weeklySeibatuRankingRewards := []WeeklySeibatuRankingReward{
|
||||
{0, 0, 0, 0, 0, 0},
|
||||
}
|
||||
for _, reward := range weeklySeibatuRankingRewards {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteInt32(reward.Unk0)
|
||||
bf.WriteInt32(reward.Unk1)
|
||||
bf.WriteUint32(reward.Unk2)
|
||||
bf.WriteInt32(reward.Unk3)
|
||||
bf.WriteInt32(reward.Unk4)
|
||||
bf.WriteInt32(reward.Unk5)
|
||||
data = append(data, bf)
|
||||
}
|
||||
doAckEarthSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfGetFixedSeibatuRankingTable(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetFixedSeibatuRankingTable)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteBytes(make([]byte, 32))
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfReadBeatLevel(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfReadBeatLevel)
|
||||
|
||||
// This response is fixed and will never change on JP,
|
||||
// but I've left it dynamic for possible other client differences.
|
||||
resp := byteframe.NewByteFrame()
|
||||
for i := 0; i < int(pkt.ValidIDCount); i++ {
|
||||
resp.WriteUint32(pkt.IDs[i])
|
||||
resp.WriteUint32(1)
|
||||
resp.WriteUint32(1)
|
||||
resp.WriteUint32(1)
|
||||
}
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfReadLastWeekBeatRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfReadLastWeekBeatRanking)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfUpdateBeatLevel(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfUpdateBeatLevel)
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfReadBeatLevelAllRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfReadBeatLevelAllRanking)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteBytes(make([]byte, 32))
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfReadBeatLevelMyRanking(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfReadBeatLevelMyRanking)
|
||||
bf := byteframe.NewByteFrame()
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
@@ -7,54 +7,124 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type TournamentInfo0 struct {
|
||||
ID uint32
|
||||
MaxPlayers uint32
|
||||
CurrentPlayers uint32
|
||||
Unk1 uint16
|
||||
TextColor uint16
|
||||
Unk2 uint32
|
||||
Time1 time.Time
|
||||
Time2 time.Time
|
||||
Time3 time.Time
|
||||
Time4 time.Time
|
||||
Time5 time.Time
|
||||
Time6 time.Time
|
||||
Unk3 uint8
|
||||
Unk4 uint8
|
||||
MinHR uint32
|
||||
MaxHR uint32
|
||||
Unk5 string
|
||||
Unk6 string
|
||||
}
|
||||
|
||||
type TournamentInfo21 struct {
|
||||
Unk0 uint32
|
||||
Unk1 uint32
|
||||
Unk2 uint32
|
||||
Unk3 uint8
|
||||
}
|
||||
|
||||
type TournamentInfo22 struct {
|
||||
Unk0 uint32
|
||||
Unk1 uint32
|
||||
Unk2 uint32
|
||||
Unk3 uint8
|
||||
Unk4 string
|
||||
}
|
||||
|
||||
func handleMsgMhfInfoTournament(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfInfoTournament)
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
tournamentInfo0 := []TournamentInfo0{}
|
||||
tournamentInfo21 := []TournamentInfo21{}
|
||||
tournamentInfo22 := []TournamentInfo22{}
|
||||
|
||||
switch pkt.Unk0 {
|
||||
case 0:
|
||||
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
|
||||
bf.WriteUint32(0) // Tied to schedule ID?
|
||||
case 1:
|
||||
|
||||
bf.WriteBytes(make([]byte, 21))
|
||||
ps.Uint8(bf, "", false)
|
||||
break
|
||||
|
||||
bf.WriteUint32(0xACEDCAFE)
|
||||
|
||||
bf.WriteUint32(5) // Active schedule?
|
||||
|
||||
bf.WriteUint32(1) // Schedule ID?
|
||||
|
||||
bf.WriteUint32(32) // Max players
|
||||
bf.WriteUint32(0) // Registered players
|
||||
|
||||
bf.WriteUint16(0)
|
||||
bf.WriteUint16(2) // Color code for schedule item
|
||||
bf.WriteUint32(0)
|
||||
|
||||
bf.WriteUint32(uint32(time.Now().Add(time.Hour * -10).Unix()))
|
||||
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix()))
|
||||
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix()))
|
||||
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix()))
|
||||
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix()))
|
||||
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix()))
|
||||
|
||||
bf.WriteBool(true) // Unk
|
||||
bf.WriteBool(false) // Cafe-only
|
||||
|
||||
bf.WriteUint32(0) // Min HR
|
||||
bf.WriteUint32(0) // Max HR
|
||||
|
||||
ps.Uint8(bf, "Test", false)
|
||||
|
||||
// ...
|
||||
bf.WriteUint32(uint32(len(tournamentInfo0)))
|
||||
for _, tinfo := range tournamentInfo0 {
|
||||
bf.WriteUint32(tinfo.ID)
|
||||
bf.WriteUint32(tinfo.MaxPlayers)
|
||||
bf.WriteUint32(tinfo.CurrentPlayers)
|
||||
bf.WriteUint16(tinfo.Unk1)
|
||||
bf.WriteUint16(tinfo.TextColor)
|
||||
bf.WriteUint32(tinfo.Unk2)
|
||||
bf.WriteUint32(uint32(tinfo.Time1.Unix()))
|
||||
bf.WriteUint32(uint32(tinfo.Time2.Unix()))
|
||||
bf.WriteUint32(uint32(tinfo.Time3.Unix()))
|
||||
bf.WriteUint32(uint32(tinfo.Time4.Unix()))
|
||||
bf.WriteUint32(uint32(tinfo.Time5.Unix()))
|
||||
bf.WriteUint32(uint32(tinfo.Time6.Unix()))
|
||||
bf.WriteUint8(tinfo.Unk3)
|
||||
bf.WriteUint8(tinfo.Unk4)
|
||||
bf.WriteUint32(tinfo.MinHR)
|
||||
bf.WriteUint32(tinfo.MaxHR)
|
||||
ps.Uint8(bf, tinfo.Unk5, true)
|
||||
ps.Uint16(bf, tinfo.Unk6, true)
|
||||
}
|
||||
case 1:
|
||||
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
|
||||
bf.WriteUint32(0) // Registered ID
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteUint32(0)
|
||||
ps.Uint8(bf, "", true)
|
||||
case 2:
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint32(uint32(len(tournamentInfo21)))
|
||||
for _, info := range tournamentInfo21 {
|
||||
bf.WriteUint32(info.Unk0)
|
||||
bf.WriteUint32(info.Unk1)
|
||||
bf.WriteUint32(info.Unk2)
|
||||
bf.WriteUint8(info.Unk3)
|
||||
}
|
||||
bf.WriteUint32(uint32(len(tournamentInfo22)))
|
||||
for _, info := range tournamentInfo22 {
|
||||
bf.WriteUint32(info.Unk0)
|
||||
bf.WriteUint32(info.Unk1)
|
||||
bf.WriteUint32(info.Unk2)
|
||||
bf.WriteUint8(info.Unk3)
|
||||
ps.Uint8(bf, info.Unk4, true)
|
||||
}
|
||||
}
|
||||
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
func handleMsgMhfEntryTournament(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfEntryTournament(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfEntryTournament)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfAcquireTournament(s *Session, p mhfpacket.MHFPacket) {}
|
||||
type TournamentReward struct {
|
||||
Unk0 uint16
|
||||
Unk1 uint16
|
||||
Unk2 uint16
|
||||
}
|
||||
|
||||
func handleMsgMhfAcquireTournament(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfAcquireTournament)
|
||||
rewards := []TournamentReward{}
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(uint8(len(rewards)))
|
||||
for _, reward := range rewards {
|
||||
bf.WriteUint16(reward.Unk0)
|
||||
bf.WriteUint16(reward.Unk1)
|
||||
bf.WriteUint16(reward.Unk2)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||
}
|
||||
|
||||
@@ -1,102 +1,477 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/common/stringsupport"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
)
|
||||
|
||||
type TowerInfoTRP struct {
|
||||
TR int32
|
||||
TRP int32
|
||||
}
|
||||
|
||||
type TowerInfoSkill struct {
|
||||
TSP int32
|
||||
Unk1 []int16 // 40
|
||||
}
|
||||
|
||||
type TowerInfoHistory struct {
|
||||
Unk0 []int16 // 5
|
||||
Unk1 []int16 // 5
|
||||
}
|
||||
|
||||
type TowerInfoLevel struct {
|
||||
Floors int32
|
||||
Unk1 int32
|
||||
Unk2 int32
|
||||
Unk3 int32
|
||||
}
|
||||
|
||||
func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetTowerInfo)
|
||||
var data []byte
|
||||
var err error
|
||||
/*
|
||||
type:
|
||||
1 == TOWER_RANK_POINT,
|
||||
2 == GET_OWN_TOWER_SKILL
|
||||
3 == GET_OWN_TOWER_LEVEL_V3
|
||||
4 == TOWER_TOUHA_HISTORY
|
||||
5 = ?
|
||||
|
||||
[] = type
|
||||
req
|
||||
resp
|
||||
|
||||
01 1d 01 fc 00 09 [00 00 00 01] 00 00 00 02 00 00 00 00
|
||||
00 12 01 fc 00 09 01 00 00 18 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00
|
||||
|
||||
01 1d 01 fc 00 0a [00 00 00 02] 00 00 00 00 00 00 00 00
|
||||
00 12 01 fc 00 0a 01 00 00 94 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
|
||||
01 1d 01 ff 00 0f [00 00 00 04] 00 00 00 00 00 00 00 00
|
||||
00 12 01 ff 00 0f 01 00 00 24 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
|
||||
01 1d 01 fc 00 0b [00 00 00 05] 00 00 00 00 00 00 00 00
|
||||
00 12 01 fc 00 0b 01 00 00 10 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
*/
|
||||
switch pkt.InfoType {
|
||||
case mhfpacket.TowerInfoTypeTowerRankPoint:
|
||||
data, err = hex.DecodeString("0A218EAD0000000000000000000000010000000000000000")
|
||||
case mhfpacket.TowerInfoTypeGetOwnTowerSkill:
|
||||
//data, err = hex.DecodeString("0A218EAD000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
data, err = hex.DecodeString("0A218EAD0000000000000000000000010000001C0000000500050000000000020000000000000000000000000000000000030003000000000003000500050000000300030003000300030003000200030001000300020002000300010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
case mhfpacket.TowerInfoTypeGetOwnTowerLevelV3:
|
||||
panic("No known response values for GetOwnTowerLevelV3")
|
||||
case mhfpacket.TowerInfoTypeTowerTouhaHistory:
|
||||
data, err = hex.DecodeString("0A218EAD0000000000000000000000010000000000000000000000000000000000000000")
|
||||
case mhfpacket.TowerInfoTypeUnk5:
|
||||
data, err = hex.DecodeString("0A218EAD000000000000000000000000")
|
||||
var data []*byteframe.ByteFrame
|
||||
type TowerInfo struct {
|
||||
TRP []TowerInfoTRP
|
||||
Skill []TowerInfoSkill
|
||||
History []TowerInfoHistory
|
||||
Level []TowerInfoLevel
|
||||
}
|
||||
|
||||
towerInfo := TowerInfo{
|
||||
TRP: []TowerInfoTRP{{0, 0}},
|
||||
Skill: []TowerInfoSkill{{0, make([]int16, 40)}},
|
||||
History: []TowerInfoHistory{{make([]int16, 5), make([]int16, 5)}},
|
||||
Level: []TowerInfoLevel{{0, 0, 0, 0}, {0, 0, 0, 0}},
|
||||
}
|
||||
|
||||
tempSkills := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
|
||||
|
||||
err := s.server.db.QueryRow(`SELECT COALESCE(tr, 0), COALESCE(trp, 0), COALESCE(tsp, 0), COALESCE(block1, 0), COALESCE(block2, 0), skills FROM tower WHERE char_id=$1
|
||||
`, s.charID).Scan(&towerInfo.TRP[0].TR, &towerInfo.TRP[0].TRP, &towerInfo.Skill[0].TSP, &towerInfo.Level[0].Floors, &towerInfo.Level[1].Floors, &tempSkills)
|
||||
if err != nil {
|
||||
stubGetNoResults(s, pkt.AckHandle)
|
||||
s.server.db.Exec(`INSERT INTO tower (char_id) VALUES ($1)`, s.charID)
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
|
||||
for i, skill := range stringsupport.CSVElems(tempSkills) {
|
||||
towerInfo.Skill[0].Unk1[i] = int16(skill)
|
||||
}
|
||||
|
||||
switch pkt.InfoType {
|
||||
case 1:
|
||||
for _, trp := range towerInfo.TRP {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteInt32(trp.TR)
|
||||
bf.WriteInt32(trp.TRP)
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 2:
|
||||
for _, skills := range towerInfo.Skill {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteInt32(skills.TSP)
|
||||
for i := range skills.Unk1 {
|
||||
bf.WriteInt16(skills.Unk1[i])
|
||||
}
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 4:
|
||||
for _, history := range towerInfo.History {
|
||||
bf := byteframe.NewByteFrame()
|
||||
for i := range history.Unk0 {
|
||||
bf.WriteInt16(history.Unk0[i])
|
||||
}
|
||||
for i := range history.Unk1 {
|
||||
bf.WriteInt16(history.Unk1[i])
|
||||
}
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 5:
|
||||
for _, level := range towerInfo.Level {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteInt32(level.Floors)
|
||||
bf.WriteInt32(level.Unk1)
|
||||
bf.WriteInt32(level.Unk2)
|
||||
bf.WriteInt32(level.Unk3)
|
||||
data = append(data, bf)
|
||||
}
|
||||
}
|
||||
doAckEarthSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostTowerInfo)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||
|
||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools {
|
||||
s.logger.Debug(
|
||||
p.Opcode().String(),
|
||||
zap.Uint32("InfoType", pkt.InfoType),
|
||||
zap.Uint32("Unk1", pkt.Unk1),
|
||||
zap.Int32("Skill", pkt.Skill),
|
||||
zap.Int32("TR", pkt.TR),
|
||||
zap.Int32("TRP", pkt.TRP),
|
||||
zap.Int32("Cost", pkt.Cost),
|
||||
zap.Int32("Unk6", pkt.Unk6),
|
||||
zap.Int32("Unk7", pkt.Unk7),
|
||||
zap.Int32("Block1", pkt.Block1),
|
||||
zap.Int64("Unk9", pkt.Unk9),
|
||||
)
|
||||
}
|
||||
|
||||
switch pkt.InfoType {
|
||||
case 2:
|
||||
skills := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
|
||||
s.server.db.QueryRow(`SELECT skills FROM tower WHERE char_id=$1`, s.charID).Scan(&skills)
|
||||
s.server.db.Exec(`UPDATE tower SET skills=$1, tsp=tsp-$2 WHERE char_id=$3`, stringsupport.CSVSetIndex(skills, int(pkt.Skill), stringsupport.CSVGetIndex(skills, int(pkt.Skill))+1), pkt.Cost, s.charID)
|
||||
case 7:
|
||||
s.server.db.Exec(`UPDATE tower SET tr=$1, trp=trp+$2, block1=block1+$3 WHERE char_id=$4`, pkt.TR, pkt.TRP, pkt.Block1, s.charID)
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
// Default missions
|
||||
var tenrouiraiData = []TenrouiraiData{
|
||||
{1, 1, 80, 0, 2, 2, 1, 1, 2, 2},
|
||||
{1, 4, 16, 0, 2, 2, 1, 1, 2, 2},
|
||||
{1, 6, 50, 0, 2, 2, 1, 0, 2, 2},
|
||||
{1, 4, 12, 50, 2, 2, 1, 1, 2, 2},
|
||||
{1, 3, 50, 0, 2, 2, 1, 1, 2, 2},
|
||||
{2, 5, 40000, 0, 2, 2, 1, 0, 2, 2},
|
||||
{1, 5, 50000, 50, 2, 2, 1, 1, 2, 2},
|
||||
{2, 1, 60, 0, 2, 2, 1, 1, 2, 2},
|
||||
{2, 3, 50, 0, 2, 1, 1, 0, 1, 2},
|
||||
{2, 3, 40, 50, 2, 1, 1, 1, 1, 2},
|
||||
{2, 4, 12, 0, 2, 1, 1, 1, 1, 2},
|
||||
{2, 6, 40, 0, 2, 1, 1, 0, 1, 2},
|
||||
{1, 1, 60, 50, 2, 1, 2, 1, 1, 2},
|
||||
{1, 5, 50000, 0, 3, 1, 2, 1, 1, 2},
|
||||
{1, 6, 50, 0, 3, 1, 2, 0, 1, 2},
|
||||
{1, 4, 16, 50, 3, 1, 2, 1, 1, 2},
|
||||
{1, 5, 50000, 0, 3, 1, 2, 1, 1, 2},
|
||||
{2, 3, 40, 0, 3, 1, 2, 0, 1, 2},
|
||||
{1, 3, 50, 50, 3, 1, 2, 1, 1, 2},
|
||||
{2, 5, 40000, 0, 3, 1, 2, 1, 1, 1},
|
||||
{2, 6, 40, 0, 3, 1, 2, 0, 1, 1},
|
||||
{2, 1, 60, 50, 3, 1, 2, 1, 1, 1},
|
||||
{2, 6, 50, 0, 3, 1, 2, 1, 1, 1},
|
||||
{2, 4, 12, 0, 3, 1, 2, 0, 1, 1},
|
||||
{1, 1, 80, 50, 3, 1, 2, 1, 1, 1},
|
||||
{1, 5, 40000, 0, 3, 1, 2, 1, 1, 1},
|
||||
{1, 3, 50, 0, 3, 1, 2, 0, 1, 1},
|
||||
{1, 4, 16, 50, 3, 1, 0, 1, 1, 1},
|
||||
{1, 6, 50, 0, 3, 1, 0, 1, 1, 1},
|
||||
{2, 3, 40, 0, 3, 1, 0, 1, 1, 1},
|
||||
{1, 1, 80, 50, 3, 1, 0, 0, 1, 1},
|
||||
{2, 5, 40000, 0, 3, 1, 0, 0, 1, 1},
|
||||
{2, 6, 40, 0, 3, 1, 0, 0, 1, 1},
|
||||
}
|
||||
|
||||
type TenrouiraiProgress struct {
|
||||
Page uint8
|
||||
Mission1 uint16
|
||||
Mission2 uint16
|
||||
Mission3 uint16
|
||||
}
|
||||
|
||||
type TenrouiraiReward struct {
|
||||
Index uint8
|
||||
Item []uint16 // 5
|
||||
Quantity []uint8 // 5
|
||||
}
|
||||
|
||||
type TenrouiraiKeyScore struct {
|
||||
Unk0 uint8
|
||||
Unk1 int32
|
||||
}
|
||||
|
||||
type TenrouiraiData struct {
|
||||
Block uint8
|
||||
Mission uint8
|
||||
// 1 = Floors climbed
|
||||
// 2 = Collect antiques
|
||||
// 3 = Open chests
|
||||
// 4 = Cats saved
|
||||
// 5 = TRP acquisition
|
||||
// 6 = Monster slays
|
||||
Goal uint16
|
||||
Cost uint16
|
||||
Skill1 uint8 // 80
|
||||
Skill2 uint8 // 40
|
||||
Skill3 uint8 // 40
|
||||
Skill4 uint8 // 20
|
||||
Skill5 uint8 // 40
|
||||
Skill6 uint8 // 50
|
||||
}
|
||||
|
||||
type TenrouiraiCharScore struct {
|
||||
Score int32
|
||||
Name string
|
||||
}
|
||||
|
||||
type TenrouiraiTicket struct {
|
||||
Unk0 uint8
|
||||
RP uint32
|
||||
Unk2 uint32
|
||||
}
|
||||
|
||||
type Tenrouirai struct {
|
||||
Progress []TenrouiraiProgress
|
||||
Reward []TenrouiraiReward
|
||||
KeyScore []TenrouiraiKeyScore
|
||||
Data []TenrouiraiData
|
||||
CharScore []TenrouiraiCharScore
|
||||
Ticket []TenrouiraiTicket
|
||||
}
|
||||
|
||||
func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
||||
// if the game gets bad responses for this it breaks the ability to save
|
||||
pkt := p.(*mhfpacket.MsgMhfGetTenrouirai)
|
||||
var data []byte
|
||||
var err error
|
||||
if pkt.Unk0 == 1 {
|
||||
data, err = hex.DecodeString("0A218EAD000000000000000000000001010000000000060010")
|
||||
} else if pkt.Unk2 == 4 {
|
||||
data, err = hex.DecodeString("0A218EAD0000000000000000000000210101005000000202010102020104001000000202010102020106003200000202010002020104000C003202020101020201030032000002020101020202059C4000000202010002020105C35000320202010102020201003C00000202010102020203003200000201010001020203002800320201010101020204000C00000201010101020206002800000201010001020101003C00320201020101020105C35000000301020101020106003200000301020001020104001000320301020101020105C350000003010201010202030028000003010200010201030032003203010201010202059C4000000301020101010206002800000301020001010201003C00320301020101010206003200000301020101010204000C000003010200010101010050003203010201010101059C40000003010201010101030032000003010200010101040010003203010001010101060032000003010001010102030028000003010001010101010050003203010000010102059C4000000301000001010206002800000301000001010010")
|
||||
} else {
|
||||
data = []byte{0x00, 0x00, 0x00, 0x00}
|
||||
s.logger.Info("GET_TENROUIRAI request for unknown type")
|
||||
var data []*byteframe.ByteFrame
|
||||
|
||||
tenrouirai := Tenrouirai{
|
||||
Progress: []TenrouiraiProgress{{1, 0, 0, 0}},
|
||||
Data: tenrouiraiData,
|
||||
Ticket: []TenrouiraiTicket{{0, 0, 0}},
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
switch pkt.Unk1 {
|
||||
case 1:
|
||||
for _, tdata := range tenrouirai.Data {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(tdata.Block)
|
||||
bf.WriteUint8(tdata.Mission)
|
||||
bf.WriteUint16(tdata.Goal)
|
||||
bf.WriteUint16(tdata.Cost)
|
||||
bf.WriteUint8(tdata.Skill1)
|
||||
bf.WriteUint8(tdata.Skill2)
|
||||
bf.WriteUint8(tdata.Skill3)
|
||||
bf.WriteUint8(tdata.Skill4)
|
||||
bf.WriteUint8(tdata.Skill5)
|
||||
bf.WriteUint8(tdata.Skill6)
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 2:
|
||||
for _, reward := range tenrouirai.Reward {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(reward.Index)
|
||||
bf.WriteUint16(reward.Item[0])
|
||||
bf.WriteUint16(reward.Item[1])
|
||||
bf.WriteUint16(reward.Item[2])
|
||||
bf.WriteUint16(reward.Item[3])
|
||||
bf.WriteUint16(reward.Item[4])
|
||||
bf.WriteUint8(reward.Quantity[0])
|
||||
bf.WriteUint8(reward.Quantity[1])
|
||||
bf.WriteUint8(reward.Quantity[2])
|
||||
bf.WriteUint8(reward.Quantity[3])
|
||||
bf.WriteUint8(reward.Quantity[4])
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 4:
|
||||
s.server.db.QueryRow(`SELECT tower_mission_page FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&tenrouirai.Progress[0].Page)
|
||||
s.server.db.QueryRow(`SELECT SUM(tower_mission_1) AS _, SUM(tower_mission_2) AS _, SUM(tower_mission_3) AS _ FROM guild_characters WHERE guild_id=$1
|
||||
`, pkt.GuildID).Scan(&tenrouirai.Progress[0].Mission1, &tenrouirai.Progress[0].Mission2, &tenrouirai.Progress[0].Mission3)
|
||||
|
||||
if tenrouirai.Progress[0].Mission1 > tenrouiraiData[(tenrouirai.Progress[0].Page*3)-3].Goal {
|
||||
tenrouirai.Progress[0].Mission1 = tenrouiraiData[(tenrouirai.Progress[0].Page*3)-3].Goal
|
||||
}
|
||||
if tenrouirai.Progress[0].Mission2 > tenrouiraiData[(tenrouirai.Progress[0].Page*3)-2].Goal {
|
||||
tenrouirai.Progress[0].Mission2 = tenrouiraiData[(tenrouirai.Progress[0].Page*3)-2].Goal
|
||||
}
|
||||
if tenrouirai.Progress[0].Mission3 > tenrouiraiData[(tenrouirai.Progress[0].Page*3)-1].Goal {
|
||||
tenrouirai.Progress[0].Mission3 = tenrouiraiData[(tenrouirai.Progress[0].Page*3)-1].Goal
|
||||
}
|
||||
|
||||
for _, progress := range tenrouirai.Progress {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(progress.Page)
|
||||
bf.WriteUint16(progress.Mission1)
|
||||
bf.WriteUint16(progress.Mission2)
|
||||
bf.WriteUint16(progress.Mission3)
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 5:
|
||||
if pkt.Unk3 > 3 {
|
||||
pkt.Unk3 %= 3
|
||||
if pkt.Unk3 == 0 {
|
||||
pkt.Unk3 = 3
|
||||
}
|
||||
}
|
||||
rows, _ := s.server.db.Query(fmt.Sprintf(`SELECT name, tower_mission_%d FROM guild_characters gc INNER JOIN characters c ON gc.character_id = c.id WHERE guild_id=$1 AND tower_mission_%d IS NOT NULL ORDER BY tower_mission_%d DESC`, pkt.Unk3, pkt.Unk3, pkt.Unk3), pkt.GuildID)
|
||||
for rows.Next() {
|
||||
temp := TenrouiraiCharScore{}
|
||||
rows.Scan(&temp.Name, &temp.Score)
|
||||
tenrouirai.CharScore = append(tenrouirai.CharScore, temp)
|
||||
}
|
||||
for _, charScore := range tenrouirai.CharScore {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteInt32(charScore.Score)
|
||||
bf.WriteBytes(stringsupport.PaddedString(charScore.Name, 14, true))
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 6:
|
||||
s.server.db.QueryRow(`SELECT tower_rp FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&tenrouirai.Ticket[0].RP)
|
||||
for _, ticket := range tenrouirai.Ticket {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(ticket.Unk0)
|
||||
bf.WriteUint32(ticket.RP)
|
||||
bf.WriteUint32(ticket.Unk2)
|
||||
data = append(data, bf)
|
||||
}
|
||||
}
|
||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||
|
||||
doAckEarthSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostTenrouirai)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||
}
|
||||
|
||||
func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) {}
|
||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools {
|
||||
s.logger.Debug(
|
||||
p.Opcode().String(),
|
||||
zap.Uint8("Unk0", pkt.Unk0),
|
||||
zap.Uint8("Op", pkt.Op),
|
||||
zap.Uint32("GuildID", pkt.GuildID),
|
||||
zap.Uint8("Unk1", pkt.Unk1),
|
||||
zap.Uint16("Floors", pkt.Floors),
|
||||
zap.Uint16("Antiques", pkt.Antiques),
|
||||
zap.Uint16("Chests", pkt.Chests),
|
||||
zap.Uint16("Cats", pkt.Cats),
|
||||
zap.Uint16("TRP", pkt.TRP),
|
||||
zap.Uint16("Slays", pkt.Slays),
|
||||
)
|
||||
}
|
||||
|
||||
func handleMsgMhfGetWeeklySeibatuRankingReward(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetWeeklySeibatuRankingReward)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
if pkt.Op == 2 {
|
||||
var page, requirement, donated int
|
||||
s.server.db.QueryRow(`SELECT tower_mission_page, tower_rp FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&page, &donated)
|
||||
|
||||
for i := 0; i < (page*3)+1; i++ {
|
||||
requirement += int(tenrouiraiData[i].Cost)
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
sd, err := GetCharacterSaveData(s, s.charID)
|
||||
if err == nil && sd != nil {
|
||||
sd.RP -= pkt.DonatedRP
|
||||
sd.Save(s)
|
||||
if donated+int(pkt.DonatedRP) >= requirement {
|
||||
s.server.db.Exec(`UPDATE guilds SET tower_mission_page=tower_mission_page+1 WHERE id=$1`, pkt.GuildID)
|
||||
s.server.db.Exec(`UPDATE guild_characters SET tower_mission_1=NULL, tower_mission_2=NULL, tower_mission_3=NULL WHERE guild_id=$1`, pkt.GuildID)
|
||||
pkt.DonatedRP = uint16(requirement - donated)
|
||||
}
|
||||
bf.WriteUint32(uint32(pkt.DonatedRP))
|
||||
s.server.db.Exec(`UPDATE guilds SET tower_rp=tower_rp+$1 WHERE id=$2`, pkt.DonatedRP, pkt.GuildID)
|
||||
} else {
|
||||
bf.WriteUint32(0)
|
||||
}
|
||||
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
||||
} else {
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
}
|
||||
|
||||
func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPresentBox)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
var data []*byteframe.ByteFrame
|
||||
/*
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
bf.WriteInt32(0)
|
||||
*/
|
||||
doAckEarthSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
type GemInfo struct {
|
||||
Gem uint16
|
||||
Quantity uint16
|
||||
}
|
||||
|
||||
type GemHistory struct {
|
||||
Gem uint16
|
||||
Message uint16
|
||||
Timestamp time.Time
|
||||
Sender string
|
||||
}
|
||||
|
||||
func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetGemInfo)
|
||||
var data []*byteframe.ByteFrame
|
||||
gemInfo := []GemInfo{}
|
||||
gemHistory := []GemHistory{}
|
||||
|
||||
tempGems := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
|
||||
s.server.db.QueryRow(`SELECT gems FROM tower WHERE char_id=$1`, s.charID).Scan(&tempGems)
|
||||
for i, v := range stringsupport.CSVElems(tempGems) {
|
||||
gemInfo = append(gemInfo, GemInfo{uint16(((i / 5) * 256) + ((i % 5) + 1)), uint16(v)})
|
||||
}
|
||||
|
||||
switch pkt.Unk0 {
|
||||
case 1:
|
||||
for _, info := range gemInfo {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(info.Gem)
|
||||
bf.WriteUint16(info.Quantity)
|
||||
data = append(data, bf)
|
||||
}
|
||||
case 2:
|
||||
for _, history := range gemHistory {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(history.Gem)
|
||||
bf.WriteUint16(history.Message)
|
||||
bf.WriteUint32(uint32(history.Timestamp.Unix()))
|
||||
bf.WriteBytes(stringsupport.PaddedString(history.Sender, 14, true))
|
||||
data = append(data, bf)
|
||||
}
|
||||
}
|
||||
doAckEarthSucceed(s, pkt.AckHandle, data)
|
||||
}
|
||||
|
||||
func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostGemInfo)
|
||||
|
||||
if s.server.erupeConfig.DevModeOptions.QuestDebugTools {
|
||||
s.logger.Debug(
|
||||
p.Opcode().String(),
|
||||
zap.Uint32("Op", pkt.Op),
|
||||
zap.Uint32("Unk1", pkt.Unk1),
|
||||
zap.Int32("Gem", pkt.Gem),
|
||||
zap.Int32("Quantity", pkt.Quantity),
|
||||
zap.Int32("CID", pkt.CID),
|
||||
zap.Int32("Message", pkt.Message),
|
||||
zap.Int32("Unk6", pkt.Unk6),
|
||||
)
|
||||
}
|
||||
|
||||
gems := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
|
||||
s.server.db.QueryRow(`SELECT gems FROM tower WHERE char_id=$1`, s.charID).Scan(&gems)
|
||||
switch pkt.Op {
|
||||
case 1: // Add gem
|
||||
i := int(((pkt.Gem / 256) * 5) + (((pkt.Gem - ((pkt.Gem / 256) * 256)) - 1) % 5))
|
||||
s.server.db.Exec(`UPDATE tower SET gems=$1 WHERE char_id=$2`, stringsupport.CSVSetIndex(gems, i, stringsupport.CSVGetIndex(gems, i)+int(pkt.Quantity)), s.charID)
|
||||
case 2: // Transfer gem
|
||||
// no way im doing this for now
|
||||
}
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {}
|
||||
func handleMsgMhfGetNotice(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfGetNotice)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
func handleMsgMhfPostNotice(s *Session, p mhfpacket.MHFPacket) {
|
||||
pkt := p.(*mhfpacket.MsgMhfPostNotice)
|
||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ type Config struct {
|
||||
Logger *zap.Logger
|
||||
DB *sqlx.DB
|
||||
DiscordBot *discordbot.DiscordBot
|
||||
ErupeConfig *config.Config
|
||||
ErupeConfig *_config.Config
|
||||
Name string
|
||||
Enable bool
|
||||
}
|
||||
@@ -43,7 +43,7 @@ type Server struct {
|
||||
Port uint16
|
||||
logger *zap.Logger
|
||||
db *sqlx.DB
|
||||
erupeConfig *config.Config
|
||||
erupeConfig *_config.Config
|
||||
acceptConns chan net.Conn
|
||||
deleteConns chan net.Conn
|
||||
sessions map[net.Conn]*Session
|
||||
|
||||
@@ -30,7 +30,7 @@ type Stage struct {
|
||||
|
||||
// Objects
|
||||
objects map[uint32]*Object
|
||||
objectIndex uint8
|
||||
objectIndex uint16
|
||||
|
||||
// Map of session -> charID.
|
||||
// These are clients that are CURRENTLY in the stage
|
||||
@@ -56,7 +56,6 @@ func NewStage(ID string) *Stage {
|
||||
clients: make(map[*Session]uint32),
|
||||
reservedClientSlots: make(map[uint32]bool),
|
||||
objects: make(map[uint32]*Object),
|
||||
objectIndex: 0,
|
||||
rawBinaryData: make(map[stageBinaryKey][]byte),
|
||||
maxPlayers: 4,
|
||||
}
|
||||
@@ -97,16 +96,10 @@ func (s *Stage) isQuest() bool {
|
||||
}
|
||||
|
||||
func (s *Stage) NextObjectID() uint32 {
|
||||
s.objectIndex = s.objectIndex + 1
|
||||
// Objects beyond 127 do not duplicate correctly
|
||||
// Indexes 0 and 127 does not update position correctly
|
||||
if s.objectIndex == 127 {
|
||||
s.objectIndex = 1
|
||||
}
|
||||
s.objectIndex++
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteUint8(s.objectIndex)
|
||||
bf.WriteUint16(0)
|
||||
obj := uint32(bf.Data()[3]) | uint32(bf.Data()[2])<<8 | uint32(bf.Data()[1])<<16 | uint32(bf.Data()[0])<<24
|
||||
return obj
|
||||
bf.WriteUint16(127)
|
||||
bf.WriteUint16(s.objectIndex)
|
||||
bf.Seek(0, 0)
|
||||
return bf.ReadUint32()
|
||||
}
|
||||
|
||||
@@ -9,14 +9,14 @@ import (
|
||||
|
||||
type DiscordBot struct {
|
||||
Session *discordgo.Session
|
||||
config *config.Config
|
||||
config *_config.Config
|
||||
logger *zap.Logger
|
||||
MainGuild *discordgo.Guild
|
||||
RealtimeChannel *discordgo.Channel
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Config *config.Config
|
||||
Config *_config.Config
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
type Server struct {
|
||||
sync.Mutex
|
||||
logger *zap.Logger
|
||||
erupeConfig *config.Config
|
||||
erupeConfig *_config.Config
|
||||
db *sqlx.DB
|
||||
listener net.Listener
|
||||
isShuttingDown bool
|
||||
@@ -28,7 +28,7 @@ type Server struct {
|
||||
type Config struct {
|
||||
Logger *zap.Logger
|
||||
DB *sqlx.DB
|
||||
ErupeConfig *config.Config
|
||||
ErupeConfig *_config.Config
|
||||
}
|
||||
|
||||
// NewServer creates a new Server type.
|
||||
@@ -68,7 +68,7 @@ func (s *Server) Shutdown() {
|
||||
s.listener.Close()
|
||||
}
|
||||
|
||||
//acceptClients handles accepting new clients in a loop.
|
||||
// acceptClients handles accepting new clients in a loop.
|
||||
func (s *Server) acceptClients() {
|
||||
for {
|
||||
conn, err := s.listener.Accept()
|
||||
|
||||
@@ -3,13 +3,12 @@ package entranceserver
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"erupe-ce/common/stringsupport"
|
||||
_config "erupe-ce/config"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"erupe-ce/common/stringsupport"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/config"
|
||||
"erupe-ce/server/channelserver"
|
||||
)
|
||||
|
||||
@@ -19,11 +18,22 @@ var season uint8
|
||||
// Server Channels
|
||||
var currentplayers uint16
|
||||
|
||||
func encodeServerInfo(config *config.Config, s *Server, local bool) []byte {
|
||||
func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte {
|
||||
serverInfos := config.Entrance.Entries
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
for serverIdx, si := range serverInfos {
|
||||
// Prevent MezFes Worlds displaying on Z1
|
||||
if config.RealClientMode <= _config.Z1 {
|
||||
if si.Type == 6 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if config.RealClientMode <= _config.G6 {
|
||||
if si.Type == 5 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
sid := (4096 + serverIdx*256) + 16
|
||||
err := s.db.QueryRow("SELECT season FROM servers WHERE server_id=$1", sid).Scan(&season)
|
||||
if err != nil {
|
||||
@@ -42,12 +52,29 @@ func encodeServerInfo(config *config.Config, s *Server, local bool) []byte {
|
||||
bf.WriteUint16(uint16(len(si.Channels)))
|
||||
bf.WriteUint8(si.Type)
|
||||
bf.WriteUint8(season)
|
||||
bf.WriteUint8(si.Recommended)
|
||||
bf.WriteUint8(0) // Prevents malformed server name
|
||||
combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...)
|
||||
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
|
||||
bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false))
|
||||
bf.WriteUint32(si.AllowedClientFlags)
|
||||
if s.erupeConfig.RealClientMode >= _config.G1 {
|
||||
bf.WriteUint8(si.Recommended)
|
||||
}
|
||||
|
||||
if s.erupeConfig.RealClientMode <= _config.F5 {
|
||||
combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...)
|
||||
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
|
||||
bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false))
|
||||
} else if s.erupeConfig.RealClientMode <= _config.GG {
|
||||
combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...)
|
||||
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
|
||||
bf.WriteUint8(uint8(len(combined)))
|
||||
bf.WriteBytes(combined)
|
||||
} else {
|
||||
bf.WriteUint8(0) // Prevents malformed server name
|
||||
combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...)
|
||||
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
|
||||
bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false))
|
||||
}
|
||||
|
||||
if s.erupeConfig.RealClientMode >= _config.GG {
|
||||
bf.WriteUint32(si.AllowedClientFlags)
|
||||
}
|
||||
|
||||
for channelIdx, ci := range si.Channels {
|
||||
sid = (4096 + serverIdx*256) + (16 + channelIdx)
|
||||
@@ -91,16 +118,39 @@ func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byt
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
func makeSv2Resp(config *config.Config, s *Server, local bool) []byte {
|
||||
func makeSv2Resp(config *_config.Config, s *Server, local bool) []byte {
|
||||
serverInfos := config.Entrance.Entries
|
||||
// Decrease by the number of MezFes Worlds
|
||||
var mf int
|
||||
if config.RealClientMode <= _config.Z1 {
|
||||
for _, si := range serverInfos {
|
||||
if si.Type == 6 {
|
||||
mf++
|
||||
}
|
||||
}
|
||||
}
|
||||
// and Return Worlds
|
||||
var ret int
|
||||
if config.RealClientMode <= _config.G6 {
|
||||
for _, si := range serverInfos {
|
||||
if si.Type == 5 {
|
||||
ret++
|
||||
}
|
||||
}
|
||||
}
|
||||
rawServerData := encodeServerInfo(config, s, local)
|
||||
|
||||
if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogOutboundMessages {
|
||||
fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(rawServerData), hex.Dump(rawServerData))
|
||||
}
|
||||
|
||||
respType := "SV2"
|
||||
if config.RealClientMode <= _config.G32 {
|
||||
respType = "SVR"
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteBytes(makeHeader(rawServerData, "SV2", uint16(len(serverInfos)), 0x00))
|
||||
bf.WriteBytes(makeHeader(rawServerData, respType, uint16(len(serverInfos)-(mf+ret)), 0x00))
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
|
||||
@@ -12,15 +12,9 @@ import (
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func (s *Server) newUserChara(username string) error {
|
||||
var id int
|
||||
err := s.db.QueryRow("SELECT id FROM users WHERE username = $1", username).Scan(&id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *Server) newUserChara(uid int) error {
|
||||
var numNewChars int
|
||||
err = s.db.QueryRow("SELECT COUNT(*) FROM characters WHERE user_id = $1 AND is_new_character = true", id).Scan(&numNewChars)
|
||||
err := s.db.QueryRow("SELECT COUNT(*) FROM characters WHERE user_id = $1 AND is_new_character = true", uid).Scan(&numNewChars)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -35,7 +29,7 @@ func (s *Server) newUserChara(username string) error {
|
||||
user_id, is_female, is_new_character, name, unk_desc_string,
|
||||
hrp, gr, weapon_type, last_login)
|
||||
VALUES($1, False, True, '', '', 0, 0, 0, $2)`,
|
||||
id,
|
||||
uid,
|
||||
uint32(time.Now().Unix()),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -60,19 +54,6 @@ func (s *Server) registerDBAccount(username string, password string) (uint32, er
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Create a base new character.
|
||||
_, err = s.db.Exec(`
|
||||
INSERT INTO characters (
|
||||
user_id, is_female, is_new_character, name, unk_desc_string,
|
||||
hrp, gr, weapon_type, last_login)
|
||||
VALUES($1, False, True, '', '', 0, 0, 0, $2)`,
|
||||
uid,
|
||||
uint32(time.Now().Unix()),
|
||||
)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return uid, nil
|
||||
}
|
||||
|
||||
@@ -90,7 +71,7 @@ type character struct {
|
||||
|
||||
func (s *Server) getCharactersForUser(uid uint32) ([]character, error) {
|
||||
characters := make([]character, 0)
|
||||
err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false ORDER BY id ASC", uid)
|
||||
err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false ORDER BY id", uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -158,9 +139,6 @@ func (s *Server) getFriendsForCharacters(chars []character) []members {
|
||||
}
|
||||
friends = append(friends, charFriends...)
|
||||
}
|
||||
if len(friends) > 255 { // Uint8
|
||||
friends = friends[:255]
|
||||
}
|
||||
return friends
|
||||
}
|
||||
|
||||
@@ -180,15 +158,12 @@ func (s *Server) getGuildmatesForCharacters(chars []character) []members {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for i, _ := range charGuildmates {
|
||||
for i := range charGuildmates {
|
||||
charGuildmates[i].CID = char.ID
|
||||
}
|
||||
guildmates = append(guildmates, charGuildmates...)
|
||||
}
|
||||
}
|
||||
if len(guildmates) > 255 { // Uint8
|
||||
guildmates = guildmates[:255]
|
||||
}
|
||||
return guildmates
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"erupe-ce/common/byteframe"
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/common/stringsupport"
|
||||
_config "erupe-ce/config"
|
||||
"erupe-ce/server/channelserver"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
@@ -13,6 +14,12 @@ import (
|
||||
func (s *Session) makeSignResponse(uid uint32) []byte {
|
||||
// Get the characters from the DB.
|
||||
chars, err := s.server.getCharactersForUser(uid)
|
||||
if len(chars) == 0 {
|
||||
err = s.server.newUserChara(uid)
|
||||
if err == nil {
|
||||
chars, err = s.server.getCharactersForUser(uid)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
s.logger.Warn("Error getting characters from DB", zap.Error(err))
|
||||
}
|
||||
@@ -30,7 +37,7 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
bf.WriteUint8(1) // resp_code
|
||||
bf.WriteUint8(uint8(SIGN_SUCCESS)) // resp_code
|
||||
if (s.server.erupeConfig.PatchServerManifest != "" && s.server.erupeConfig.PatchServerFile != "") || s.client == PS3 {
|
||||
bf.WriteUint8(2)
|
||||
} else {
|
||||
@@ -78,15 +85,23 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
|
||||
bf.WriteBool(true) // Use uint16 GR, no reason not to
|
||||
bf.WriteBytes(stringsupport.PaddedString(char.Name, 16, true)) // Character name
|
||||
bf.WriteBytes(stringsupport.PaddedString(char.UnkDescString, 32, false)) // unk str
|
||||
bf.WriteUint16(char.GR)
|
||||
bf.WriteUint16(0) // Unk
|
||||
if s.server.erupeConfig.RealClientMode >= _config.G7 {
|
||||
bf.WriteUint16(char.GR)
|
||||
bf.WriteUint8(0) // Unk
|
||||
bf.WriteUint8(0) // Unk
|
||||
}
|
||||
}
|
||||
|
||||
friends := s.server.getFriendsForCharacters(chars)
|
||||
if len(friends) == 0 {
|
||||
bf.WriteUint8(0)
|
||||
} else {
|
||||
bf.WriteUint8(uint8(len(friends)))
|
||||
if len(friends) > 255 {
|
||||
bf.WriteUint8(255)
|
||||
bf.WriteUint16(uint16(len(friends)))
|
||||
} else {
|
||||
bf.WriteUint8(uint8(len(friends)))
|
||||
}
|
||||
for _, friend := range friends {
|
||||
bf.WriteUint32(friend.CID)
|
||||
bf.WriteUint32(friend.ID)
|
||||
@@ -98,7 +113,12 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
|
||||
if len(guildmates) == 0 {
|
||||
bf.WriteUint8(0)
|
||||
} else {
|
||||
bf.WriteUint8(uint8(len(guildmates)))
|
||||
if len(guildmates) > 255 {
|
||||
bf.WriteUint8(255)
|
||||
bf.WriteUint16(uint16(len(guildmates)))
|
||||
} else {
|
||||
bf.WriteUint8(uint8(len(guildmates)))
|
||||
}
|
||||
for _, guildmate := range guildmates {
|
||||
bf.WriteUint32(guildmate.CID)
|
||||
bf.WriteUint32(guildmate.ID)
|
||||
@@ -107,12 +127,12 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
|
||||
}
|
||||
|
||||
if s.server.erupeConfig.HideLoginNotice {
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteBool(false)
|
||||
} else {
|
||||
bf.WriteUint8(uint8(len(s.server.erupeConfig.LoginNotices)))
|
||||
for _, notice := range s.server.erupeConfig.LoginNotices {
|
||||
ps.Uint32(bf, notice, true)
|
||||
}
|
||||
bf.WriteBool(true)
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteUint8(0)
|
||||
ps.Uint16(bf, strings.Join(s.server.erupeConfig.LoginNotices[:], "<PAGE>"), true)
|
||||
}
|
||||
|
||||
bf.WriteUint32(s.server.getLastCID(uid))
|
||||
@@ -132,35 +152,37 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
|
||||
bf.WriteUint16(0x4E20)
|
||||
ps.Uint16(bf, "", false) // unk ipv4
|
||||
bf.WriteUint32(uint32(s.server.getReturnExpiry(uid).Unix()))
|
||||
bf.WriteUint32(0x00000000)
|
||||
bf.WriteUint32(0x0A5197DF) // unk id
|
||||
bf.WriteUint32(0)
|
||||
|
||||
mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent
|
||||
alt := s.server.erupeConfig.DevModeOptions.MezFesAlt
|
||||
if mezfes {
|
||||
// We can just use the start timestamp as the event ID
|
||||
bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix()))
|
||||
// Start time
|
||||
bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix()))
|
||||
// End time
|
||||
bf.WriteUint32(uint32(channelserver.TimeWeekNext().Unix()))
|
||||
bf.WriteUint8(2) // Unk
|
||||
bf.WriteUint32(20) // Single tickets
|
||||
bf.WriteUint32(10) // Group tickets
|
||||
bf.WriteUint8(8) // Stalls open
|
||||
bf.WriteUint8(0xA) // Unk
|
||||
bf.WriteUint8(0x3) // Pachinko
|
||||
bf.WriteUint8(0x6) // Nyanrendo
|
||||
bf.WriteUint8(0x9) // Point stall
|
||||
bf.WriteUint8(2) // Unk
|
||||
bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MezfesSoloTickets)
|
||||
bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MezfesGroupTickets)
|
||||
bf.WriteUint8(8) // Stalls open
|
||||
bf.WriteUint8(10) // Stall Map
|
||||
bf.WriteUint8(3) // Pachinko
|
||||
bf.WriteUint8(6) // Nyanrendo
|
||||
bf.WriteUint8(9) // Point stall
|
||||
if alt {
|
||||
bf.WriteUint8(0x2) // Tokotoko
|
||||
bf.WriteUint8(2) // Tokotoko Partnya
|
||||
} else {
|
||||
bf.WriteUint8(0x4) // Volpakkun
|
||||
bf.WriteUint8(4) // Volpakkun Together
|
||||
}
|
||||
bf.WriteUint8(0x8) // Battle cats
|
||||
bf.WriteUint8(0x5) // Gook
|
||||
bf.WriteUint8(0x7) // Honey
|
||||
bf.WriteUint8(8) // Dokkan Battle Cats
|
||||
bf.WriteUint8(5) // Goocoo Scoop
|
||||
bf.WriteUint8(7) // Honey Panic
|
||||
} else {
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteUint32(0)
|
||||
}
|
||||
return bf.Data()
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func (s *Session) handlePacket(pkt []byte) error {
|
||||
case "VITASGN:100":
|
||||
s.client = VITA
|
||||
s.handlePSSGN(bf)
|
||||
case "WIIUSGN:100":
|
||||
case "WIIUSGN:100", "WIIUSGN:000":
|
||||
s.client = WIIU
|
||||
s.handleWIIUSGN(bf)
|
||||
case "VITACOGLNK:100":
|
||||
@@ -124,6 +124,13 @@ func (s *Session) handleWIIUSGN(bf *byteframe.ByteFrame) {
|
||||
}
|
||||
|
||||
func (s *Session) handlePSSGN(bf *byteframe.ByteFrame) {
|
||||
// Prevent reading malformed request
|
||||
if len(bf.DataFromCurrent()) < 128 {
|
||||
resp := byteframe.NewByteFrame()
|
||||
resp.WriteUint8(uint8(SIGN_EABORT))
|
||||
s.cryptConn.SendPacket(resp.Data())
|
||||
return
|
||||
}
|
||||
_ = bf.ReadNullTerminatedBytes() // VITA = 0000000256, PS3 = 0000000255
|
||||
_ = bf.ReadBytes(2) // VITA = 1, PS3 = !
|
||||
_ = bf.ReadBytes(82)
|
||||
|
||||
@@ -16,14 +16,14 @@ import (
|
||||
type Config struct {
|
||||
Logger *zap.Logger
|
||||
DB *sqlx.DB
|
||||
ErupeConfig *config.Config
|
||||
ErupeConfig *_config.Config
|
||||
}
|
||||
|
||||
// Server is a MHF sign server.
|
||||
type Server struct {
|
||||
sync.Mutex
|
||||
logger *zap.Logger
|
||||
erupeConfig *config.Config
|
||||
erupeConfig *_config.Config
|
||||
sessions map[int]*Session
|
||||
db *sqlx.DB
|
||||
listener net.Listener
|
||||
|
||||
@@ -18,14 +18,14 @@ import (
|
||||
type Config struct {
|
||||
Logger *zap.Logger
|
||||
DB *sqlx.DB
|
||||
ErupeConfig *config.Config
|
||||
ErupeConfig *_config.Config
|
||||
}
|
||||
|
||||
// Server is the MHF custom launcher sign server.
|
||||
type Server struct {
|
||||
sync.Mutex
|
||||
logger *zap.Logger
|
||||
erupeConfig *config.Config
|
||||
erupeConfig *_config.Config
|
||||
db *sqlx.DB
|
||||
httpServer *http.Server
|
||||
isShuttingDown bool
|
||||
|
||||
Reference in New Issue
Block a user