diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..6dd29b7f8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +bin/ \ No newline at end of file diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index e69ee1705..96c9b083f 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -10,16 +10,17 @@ on: - 'go.mod' - 'go.sum' - 'main.go' + - '.github/workflows/go.yml' jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: '1.21' @@ -27,7 +28,7 @@ jobs: run: env GOOS=linux GOARCH=amd64 go build -v - name: Upload Linux-amd64 artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Linux-amd64 path: | @@ -42,7 +43,7 @@ jobs: run: env GOOS=windows GOARCH=amd64 go build -v - name: Upload Windows-amd64 artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: Windows-amd64 path: | diff --git a/config.json b/config.json index 2b836ab03..a1951e791 100644 --- a/config.json +++ b/config.json @@ -21,6 +21,7 @@ "QuestCacheExpiry": 300, "CommandPrefix": "!", "AutoCreateAccount": true, + "LoopDelay": 50, "DefaultCourses": [1, 23, 24], "EarthStatus": 0, "EarthID": 0, @@ -169,6 +170,11 @@ "Enabled": true, "Description": "Toggle the Quest timer", "Prefix": "timer" + }, { + "Name": "Playtime", + "Enabled": true, + "Description": "Show your playtime", + "Prefix": "playtime" } ], "Courses": [ diff --git a/config/config.go b/config/config.go index 52642956b..f7c48f88f 100644 --- a/config/config.go +++ b/config/config.go @@ -81,6 +81,7 @@ type Config struct { QuestCacheExpiry int // Number of seconds to keep quest data cached CommandPrefix string // The prefix for commands AutoCreateAccount bool // Automatically create accounts if they don't exist + LoopDelay int // Delay in milliseconds between each loop iteration DefaultCourses []uint16 EarthStatus int32 EarthID int32 diff --git a/docker/README.md b/docker/README.md index 3f45a12c0..11c018215 100644 --- a/docker/README.md +++ b/docker/README.md @@ -64,3 +64,7 @@ if you want all the logs and you want it to be in an attached state ```bash docker-compose up ``` + + +# Troubleshooting +Q: My Postgres will not populate. A: You're setup.sh is maybe saved as CRLF it needs to be saved as LF. \ No newline at end of file diff --git a/erupe.ico b/erupe.ico index e1358c741..0ee67fff7 100644 Binary files a/erupe.ico and b/erupe.ico differ diff --git a/go.mod b/go.mod index 5047c6609..f8a430ebd 100644 --- a/go.mod +++ b/go.mod @@ -10,9 +10,9 @@ require ( github.com/lib/pq v1.10.9 github.com/spf13/viper v1.17.0 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.17.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa - golang.org/x/text v0.14.0 + golang.org/x/text v0.21.0 ) require ( @@ -31,8 +31,8 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.18.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sys v0.28.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d2cc39776..e707b912a 100644 --- a/go.sum +++ b/go.sum @@ -220,8 +220,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -289,8 +289,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -345,8 +345,8 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -356,8 +356,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/rsrc_windows_amd64.syso b/rsrc_windows_amd64.syso index 012891568..2fcd48654 100644 Binary files a/rsrc_windows_amd64.syso and b/rsrc_windows_amd64.syso differ diff --git a/schemas/bundled-schema/NetcafeDefaults.sql b/schemas/bundled-schema/NetcafeDefaults.sql index 458cfa7b9..dd0b101b2 100644 --- a/schemas/bundled-schema/NetcafeDefaults.sql +++ b/schemas/bundled-schema/NetcafeDefaults.sql @@ -1,13 +1,15 @@ BEGIN; +TRUNCATE public.cafebonus; + INSERT INTO public.cafebonus (time_req, item_type, item_id, quantity) VALUES - (1800, 17, 0, 250), - (3600, 17, 0, 500), - (7200, 17, 0, 1000), - (10800, 17, 0, 1500), - (18000, 17, 0, 1750), - (28800, 17, 0, 3000), - (43200, 17, 0, 4000); + (1800, 17, 0, 50), + (3600, 17, 0, 100), + (7200, 17, 0, 200), + (10800, 17, 0, 300), + (18000, 17, 0, 350), + (28800, 17, 0, 500), + (43200, 17, 0, 500); END; \ No newline at end of file diff --git a/schemas/patch-schema/23-rework-distributions-2.sql b/schemas/patch-schema/23-rework-distributions-2.sql new file mode 100644 index 000000000..da6250eb0 --- /dev/null +++ b/schemas/patch-schema/23-rework-distributions-2.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE distribution ADD COLUMN rights INTEGER; +ALTER TABLE distribution ADD COLUMN selection BOOLEAN; + +END; \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 9572817f5..dd6197327 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -88,7 +88,7 @@ func updateRights(s *Session) { Rights: s.courses, UnkSize: 0, } - s.QueueSendMHF(update) + s.QueueSendMHFNonBlocking(update) } func handleMsgHead(s *Session, p mhfpacket.MHFPacket) {} @@ -143,7 +143,6 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { s.token = pkt.LoginTokenString s.Unlock() - updateRights(s) bf := byteframe.NewByteFrame() bf.WriteUint32(uint32(TimeAdjusted().Unix())) // Unix timestamp @@ -193,7 +192,7 @@ func logoutPlayer(s *Session) { for _, sess := range s.server.sessions { for rSlot := range stage.reservedClientSlots { if sess.charID == rSlot && sess.stage != nil && sess.stage.id[3:5] != "Qs" { - sess.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{}) + sess.QueueSendMHFNonBlocking(&mhfpacket.MsgSysStageDestruct{}) } } } diff --git a/server/channelserver/handlers_cafe.go b/server/channelserver/handlers_cafe.go index 406732371..d5296fc37 100644 --- a/server/channelserver/handlers_cafe.go +++ b/server/channelserver/handlers_cafe.go @@ -166,7 +166,7 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) { FROM characters ch WHERE ch.id = $1 ) >= time_req`, s.charID, TimeAdjusted().Unix()-s.sessionStart) - if err != nil { + if err != nil || !mhfcourse.CourseExists(30, s.courses) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } else { for rows.Next() { diff --git a/server/channelserver/handlers_cast_binary.go b/server/channelserver/handlers_cast_binary.go index 17815dc30..a3f2ecfb6 100644 --- a/server/channelserver/handlers_cast_binary.go +++ b/server/channelserver/handlers_cast_binary.go @@ -82,7 +82,7 @@ func sendServerChatMessage(s *Session, message string) { RawDataPayload: bf.Data(), } - s.QueueSendMHF(castedBin) + s.QueueSendMHFNonBlocking(castedBin) } func parseChatCommand(s *Session, command string) { @@ -198,7 +198,7 @@ func parseChatCommand(s *Session, command string) { temp.Build(deleteNotif, s.clientContext) } deleteNotif.WriteUint16(uint16(network.MSG_SYS_END)) - s.QueueSend(deleteNotif.Data()) + s.QueueSendNonBlocking(deleteNotif.Data()) time.Sleep(500 * time.Millisecond) reloadNotif := byteframe.NewByteFrame() for _, session := range s.server.sessions { @@ -233,7 +233,7 @@ func parseChatCommand(s *Session, command string) { temp.Build(reloadNotif, s.clientContext) } reloadNotif.WriteUint16(uint16(network.MSG_SYS_END)) - s.QueueSend(reloadNotif.Data()) + s.QueueSendNonBlocking(reloadNotif.Data()) } else { sendDisabledCommandMessage(s, commands["Reload"]) } @@ -381,7 +381,7 @@ func parseChatCommand(s *Session, command string) { payload.WriteInt16(int16(x)) // X payload.WriteInt16(int16(y)) // Y payloadBytes := payload.Data() - s.QueueSendMHF(&mhfpacket.MsgSysCastedBinary{ + s.QueueSendMHFNonBlocking(&mhfpacket.MsgSysCastedBinary{ CharID: s.charID, MessageType: BinaryMessageTypeState, RawDataPayload: payloadBytes, @@ -407,6 +407,13 @@ func parseChatCommand(s *Session, command string) { } else { sendDisabledCommandMessage(s, commands["Discord"]) } + case commands["Playtime"].Prefix: + if commands["Playtime"].Enabled || s.isOp() { + playtime := s.playtime + uint32(time.Now().Sub(s.playtimeTime).Seconds()) + sendServerChatMessage(s, fmt.Sprintf(s.server.i18n.commands.playtime, playtime/60/60, playtime/60%60, playtime%60)) + } else { + sendDisabledCommandMessage(s, commands["Playtime"]) + } case commands["Help"].Prefix: if commands["Help"].Enabled || s.isOp() { for _, command := range commands { @@ -532,7 +539,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { char := s.server.FindSessionByCharID(targetID) if char != nil { - char.QueueSendMHF(resp) + char.QueueSendMHFNonBlocking(resp) } } default: diff --git a/server/channelserver/handlers_character.go b/server/channelserver/handlers_character.go index 795fc05e1..8672b94a5 100644 --- a/server/channelserver/handlers_character.go +++ b/server/channelserver/handlers_character.go @@ -23,6 +23,7 @@ const ( pGalleryData // +1748 pToreData // +240 pGardenData // +68 + pPlaytime // +4 pWeaponType // +1 pWeaponID // +2 pHR // +2 @@ -45,6 +46,7 @@ type CharacterSaveData struct { GalleryData []byte ToreData []byte GardenData []byte + Playtime uint32 WeaponType uint8 WeaponID uint16 HR uint16 @@ -59,6 +61,7 @@ func getPointers() map[SavePointer]int { pointers := map[SavePointer]int{pGender: 81, lBookshelfData: 5576} switch _config.ErupeConfig.RealClientMode { case _config.ZZ: + pointers[pPlaytime] = 128356 pointers[pWeaponID] = 128522 pointers[pWeaponType] = 128789 pointers[pHouseTier] = 129900 @@ -74,6 +77,7 @@ func getPointers() map[SavePointer]int { case _config.Z2, _config.Z1, _config.G101, _config.G10, _config.G91, _config.G9, _config.G81, _config.G8, _config.G7, _config.G61, _config.G6, _config.G52, _config.G51, _config.G5, _config.GG, _config.G32, _config.G31, _config.G3, _config.G2, _config.G1: + pointers[pPlaytime] = 92356 pointers[pWeaponID] = 92522 pointers[pWeaponType] = 92789 pointers[pHouseTier] = 93900 @@ -87,6 +91,7 @@ func getPointers() map[SavePointer]int { pointers[pRP] = 106614 pointers[pKQF] = 110720 case _config.F5, _config.F4: + pointers[pPlaytime] = 60356 pointers[pWeaponID] = 60522 pointers[pWeaponType] = 60789 pointers[pHouseTier] = 61900 @@ -98,6 +103,7 @@ func getPointers() map[SavePointer]int { pointers[pGardenData] = 74424 pointers[pRP] = 74614 case _config.S6: + pointers[pPlaytime] = 12356 pointers[pWeaponID] = 12522 pointers[pWeaponType] = 12789 pointers[pHouseTier] = 13900 @@ -231,6 +237,7 @@ func (save *CharacterSaveData) updateStructWithSaveData() { 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.Playtime = binary.LittleEndian.Uint32(save.decompSave[save.Pointers[pPlaytime] : save.Pointers[pPlaytime]+4]) save.WeaponType = save.decompSave[save.Pointers[pWeaponType]] save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pWeaponID] : save.Pointers[pWeaponID]+2]) save.HR = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHR] : save.Pointers[pHR]+2]) diff --git a/server/channelserver/handlers_data.go b/server/channelserver/handlers_data.go index fd41e1366..0d41c42ca 100644 --- a/server/channelserver/handlers_data.go +++ b/server/channelserver/handlers_data.go @@ -54,6 +54,9 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { } characterSaveData.updateStructWithSaveData() + s.playtime = characterSaveData.Playtime + s.playtimeTime = time.Now() + // Bypass name-checker if new if characterSaveData.IsNewCharacter == true { s.Name = characterSaveData.Name diff --git a/server/channelserver/handlers_distitem.go b/server/channelserver/handlers_distitem.go index 078598719..3e2417d34 100644 --- a/server/channelserver/handlers_distitem.go +++ b/server/channelserver/handlers_distitem.go @@ -13,6 +13,7 @@ import ( type Distribution struct { ID uint32 `db:"id"` Deadline time.Time `db:"deadline"` + Rights uint32 `db:"rights"` TimesAcceptable uint16 `db:"times_acceptable"` TimesAccepted uint16 `db:"times_accepted"` MinHR int16 `db:"min_hr"` @@ -23,7 +24,7 @@ type Distribution struct { MaxGR int16 `db:"max_gr"` EventName string `db:"event_name"` Description string `db:"description"` - Data []byte `db:"data"` + Selection bool `db:"selection"` } func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) { @@ -32,7 +33,7 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) { var itemDists []Distribution bf := byteframe.NewByteFrame() rows, err := s.server.db.Queryx(` - SELECT d.id, event_name, description, times_acceptable, + SELECT d.id, event_name, description, COALESCE(rights, 0) AS rights, COALESCE(selection, false) AS selection, times_acceptable, COALESCE(min_hr, -1) AS min_hr, COALESCE(max_hr, -1) AS max_hr, COALESCE(min_sr, -1) AS min_sr, COALESCE(max_sr, -1) AS max_sr, COALESCE(min_gr, -1) AS min_gr, COALESCE(max_gr, -1) AS max_gr, @@ -60,7 +61,7 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) { for _, dist := range itemDists { bf.WriteUint32(dist.ID) bf.WriteUint32(uint32(dist.Deadline.Unix())) - bf.WriteUint32(0) // Unk + bf.WriteUint32(dist.Rights) bf.WriteUint16(dist.TimesAcceptable) bf.WriteUint16(dist.TimesAccepted) if _config.ErupeConfig.RealClientMode >= _config.G9 { @@ -79,7 +80,11 @@ func handleMsgMhfEnumerateDistItem(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint16(0) // Unk } if _config.ErupeConfig.RealClientMode >= _config.G8 { - bf.WriteUint8(0) // Unk + if dist.Selection { + bf.WriteUint8(2) // Selection + } else { + bf.WriteUint8(0) + } } if _config.ErupeConfig.RealClientMode >= _config.G7 { bf.WriteUint16(0) // Unk diff --git a/server/channelserver/handlers_festa.go b/server/channelserver/handlers_festa.go index eecd268e3..d5cba4d90 100644 --- a/server/channelserver/handlers_festa.go +++ b/server/channelserver/handlers_festa.go @@ -292,7 +292,7 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { } else { bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MaximumFP) } - bf.WriteUint16(500) + bf.WriteUint16(100) // Reward multiplier (%) var temp uint32 bf.WriteUint16(4) diff --git a/server/channelserver/handlers_guild.go b/server/channelserver/handlers_guild.go index 096419137..873560ad4 100644 --- a/server/channelserver/handlers_guild.go +++ b/server/channelserver/handlers_guild.go @@ -971,14 +971,21 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint8(0) bf.WriteUint8(0) - bf.WriteBool(!guild.Recruiting) + flags := uint8(0) + if !guild.Recruiting { + flags |= 0x01 + } + //if guild.Suspended { + // flags |= 0x02 + //} + bf.WriteUint8(flags) if characterGuildData == nil || characterGuildData.IsApplicant { - bf.WriteUint16(0x00) + bf.WriteUint16(0) } else if guild.LeaderCharID == s.charID { - bf.WriteUint16(0x01) + bf.WriteUint16(1) } else { - bf.WriteUint16(0x02) + bf.WriteUint16(2) } bf.WriteUint32(uint32(guild.CreatedAt.Unix())) @@ -1098,20 +1105,25 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { bf.WriteUint32(applicant.CharID) bf.WriteUint32(0) bf.WriteUint16(applicant.HR) - bf.WriteUint16(applicant.GR) + if s.server.erupeConfig.RealClientMode >= _config.G10 { + bf.WriteUint16(applicant.GR) + } ps.Uint8(bf, applicant.Name, true) } } - type UnkGuildInfo struct { - Unk0 uint8 + type Activity struct { + Pass uint8 Unk1 uint8 Unk2 uint8 } - unkGuildInfo := []UnkGuildInfo{} - bf.WriteUint8(uint8(len(unkGuildInfo))) - for _, info := range unkGuildInfo { - bf.WriteUint8(info.Unk0) + activity := []Activity{ + // 1,0,0 = ok + // 0,0,0 = ng + } + bf.WriteUint8(uint8(len(activity))) + for _, info := range activity { + bf.WriteUint8(info.Pass) bf.WriteUint8(info.Unk1) bf.WriteUint8(info.Unk2) } @@ -1159,7 +1171,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } else { - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 5)) + doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) } } @@ -1526,7 +1538,7 @@ func handleMsgMhfGetGuildManageRight(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetGuildManageRight) guild, err := GetGuildInfoByCharacterId(s, s.charID) - if guild == nil && s.prevGuildID != 0 { + if guild == nil || s.prevGuildID != 0 { guild, err = GetGuildInfoByID(s, s.prevGuildID) s.prevGuildID = 0 if guild == nil || err != nil { diff --git a/server/channelserver/handlers_guild_member.go b/server/channelserver/handlers_guild_member.go index f4cb04175..436a6e6cb 100644 --- a/server/channelserver/handlers_guild_member.go +++ b/server/channelserver/handlers_guild_member.go @@ -61,35 +61,41 @@ func (gm *GuildMember) Save(s *Session) error { } const guildMembersSelectSQL = ` -SELECT * FROM ( - SELECT - g.id AS guild_id, - joined_at, - COALESCE((SELECT SUM(souls) FROM festa_submissions fs WHERE fs.character_id=c.id), 0) AS souls, - COALESCE(rp_today, 0) AS rp_today, - COALESCE(rp_yesterday, 0) AS rp_yesterday, - c.name, - c.id AS character_id, - COALESCE(order_index, 0) AS order_index, - c.last_login, - COALESCE(recruiter, false) AS recruiter, - COALESCE(avoid_leadership, false) AS avoid_leadership, - c.hr, - c.gr, - c.weapon_id, - c.weapon_type, - EXISTS(SELECT 1 FROM guild_applications ga WHERE ga.character_id=c.id AND application_type='applied') AS is_applicant, - CASE WHEN g.leader_id = c.id THEN true ELSE false END AS is_leader +SELECT + COALESCE(g.id, 0) AS guild_id, + joined_at, + COALESCE((SELECT SUM(souls) FROM festa_submissions fs WHERE fs.character_id=c.id), 0) AS souls, + COALESCE(rp_today, 0) AS rp_today, + COALESCE(rp_yesterday, 0) AS rp_yesterday, + c.name, + c.id AS character_id, + COALESCE(order_index, 0) AS order_index, + c.last_login, + COALESCE(recruiter, false) AS recruiter, + COALESCE(avoid_leadership, false) AS avoid_leadership, + c.hr, + c.gr, + c.weapon_id, + c.weapon_type, + CASE WHEN g.leader_id = c.id THEN true ELSE false END AS is_leader, + character.is_applicant + FROM ( + SELECT character_id, true as is_applicant, guild_id + FROM guild_applications ga + WHERE ga.application_type = 'applied' + UNION + SELECT character_id, false as is_applicant, guild_id FROM guild_characters gc - LEFT JOIN characters c ON c.id = gc.character_id - LEFT JOIN guilds g ON g.id = gc.guild_id -) AS subquery + ) character + JOIN characters c on character.character_id = c.id + LEFT JOIN guild_characters gc ON gc.character_id = character.character_id + LEFT JOIN guilds g ON g.id = gc.guild_id ` func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMember, error) { rows, err := s.server.db.Queryx(fmt.Sprintf(` %s - WHERE guild_id = $1 AND is_applicant = $2 + WHERE character.guild_id = $1 AND is_applicant = $2 `, guildMembersSelectSQL), guildID, applicants) if err != nil { @@ -115,7 +121,7 @@ func GetGuildMembers(s *Session, guildID uint32, applicants bool) ([]*GuildMembe } func GetCharacterGuildData(s *Session, charID uint32) (*GuildMember, error) { - rows, err := s.server.db.Queryx(fmt.Sprintf("%s WHERE character_id=$1", guildMembersSelectSQL), charID) + rows, err := s.server.db.Queryx(fmt.Sprintf("%s WHERE character.character_id=$1", guildMembersSelectSQL), charID) if err != nil { s.logger.Error(fmt.Sprintf("failed to retrieve membership data for character '%d'", charID)) diff --git a/server/channelserver/handlers_mail.go b/server/channelserver/handlers_mail.go index a596d927b..41f721a1e 100644 --- a/server/channelserver/handlers_mail.go +++ b/server/channelserver/handlers_mail.go @@ -185,7 +185,7 @@ func SendMailNotification(s *Session, m *Mail, recipient *Session) { castedBinary.Build(bf, s.clientContext) - recipient.QueueSendMHF(castedBinary) + recipient.QueueSendMHFNonBlocking(castedBinary) } func getCharacterName(s *Session, charID uint32) string { diff --git a/server/channelserver/handlers_mercenary.go b/server/channelserver/handlers_mercenary.go index 19c358c52..7d92a7d86 100644 --- a/server/channelserver/handlers_mercenary.go +++ b/server/channelserver/handlers_mercenary.go @@ -21,7 +21,6 @@ func handleMsgMhfLoadPartner(s *Session, p mhfpacket.MHFPacket) { data = make([]byte, 9) } doAckBufSucceed(s, pkt.AckHandle, data) - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } func handleMsgMhfSavePartner(s *Session, p mhfpacket.MHFPacket) { @@ -157,60 +156,60 @@ func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfReadMercenaryW) - if pkt.Op > 0 { - bf := byteframe.NewByteFrame() - var pactID uint32 - var name string - var cid uint32 + bf := byteframe.NewByteFrame() - s.server.db.QueryRow("SELECT pact_id FROM characters WHERE id=$1", s.charID).Scan(&pactID) - if pactID > 0 { - s.server.db.QueryRow("SELECT name, id FROM characters WHERE rasta_id = $1", pactID).Scan(&name, &cid) - bf.WriteUint8(1) // numLends - bf.WriteUint32(pactID) - bf.WriteUint32(cid) - bf.WriteBool(false) // ? - bf.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -8).Unix())) - bf.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -1).Unix())) - bf.WriteBytes(stringsupport.PaddedString(name, 18, true)) - } else { - bf.WriteUint8(0) - } - - if pkt.Op < 2 { - var loans uint8 - temp := byteframe.NewByteFrame() - rows, _ := s.server.db.Query("SELECT name, id, pact_id FROM characters WHERE pact_id=(SELECT rasta_id FROM characters WHERE id=$1)", s.charID) - for rows.Next() { - loans++ - rows.Scan(&name, &cid, &pactID) - temp.WriteUint32(pactID) - temp.WriteUint32(cid) - temp.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -8).Unix())) - temp.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * -1).Unix())) - temp.WriteBytes(stringsupport.PaddedString(name, 18, true)) - } - bf.WriteUint8(loans) - bf.WriteBytes(temp.Data()) - } - doAckBufSucceed(s, pkt.AckHandle, bf.Data()) - return - } - var data []byte - var gcp uint32 - s.server.db.QueryRow("SELECT savemercenary FROM characters WHERE id=$1", s.charID).Scan(&data) - s.server.db.QueryRow("SELECT COALESCE(gcp, 0) FROM characters WHERE id=$1", s.charID).Scan(&gcp) - - resp := byteframe.NewByteFrame() - resp.WriteUint16(0) - if len(data) == 0 { - resp.WriteBool(false) + var pactID, cid uint32 + var name string + s.server.db.QueryRow("SELECT pact_id FROM characters WHERE id=$1", s.charID).Scan(&pactID) + if pactID > 0 { + s.server.db.QueryRow("SELECT name, id FROM characters WHERE rasta_id = $1", pactID).Scan(&name, &cid) + bf.WriteUint8(1) // numLends + bf.WriteUint32(pactID) + bf.WriteUint32(cid) + bf.WriteBool(true) // Escort enabled + bf.WriteUint32(uint32(TimeAdjusted().Unix())) + bf.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * 7).Unix())) + bf.WriteBytes(stringsupport.PaddedString(name, 18, true)) } else { - resp.WriteBool(true) - resp.WriteBytes(data) + bf.WriteUint8(0) } - resp.WriteUint32(gcp) - doAckBufSucceed(s, pkt.AckHandle, resp.Data()) + + if pkt.Op != 2 && pkt.Op != 5 { + var loans uint8 + temp := byteframe.NewByteFrame() + rows, _ := s.server.db.Query("SELECT name, id, pact_id FROM characters WHERE pact_id=(SELECT rasta_id FROM characters WHERE id=$1)", s.charID) + for rows.Next() { + err := rows.Scan(&name, &cid, &pactID) + if err != nil { + continue + } + loans++ + temp.WriteUint32(pactID) + temp.WriteUint32(cid) + temp.WriteUint32(uint32(TimeAdjusted().Unix())) + temp.WriteUint32(uint32(TimeAdjusted().Add(time.Hour * 24 * 7).Unix())) + temp.WriteBytes(stringsupport.PaddedString(name, 18, true)) + } + bf.WriteUint8(loans) + bf.WriteBytes(temp.Data()) + + if pkt.Op != 1 && pkt.Op != 4 { + var data []byte + var gcp uint32 + s.server.db.QueryRow("SELECT savemercenary FROM characters WHERE id=$1", s.charID).Scan(&data) + s.server.db.QueryRow("SELECT COALESCE(gcp, 0) FROM characters WHERE id=$1", s.charID).Scan(&gcp) + + if len(data) == 0 { + bf.WriteBool(false) + } else { + bf.WriteBool(true) + bf.WriteBytes(data) + } + bf.WriteUint32(gcp) + } + } + + doAckBufSucceed(s, pkt.AckHandle, bf.Data()) } func handleMsgMhfReadMercenaryM(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/handlers_quest.go b/server/channelserver/handlers_quest.go index 769dea5dd..b2515e03f 100644 --- a/server/channelserver/handlers_quest.go +++ b/server/channelserver/handlers_quest.go @@ -238,8 +238,10 @@ func loadQuestFile(s *Session, questId int) []byte { } questBody.WriteBytes(newStrings.Data()) + s.server.questCacheLock.Lock() s.server.questCacheData[questId] = questBody.Data() s.server.questCacheTime[questId] = time.Now() + s.server.questCacheLock.Unlock() return questBody.Data() } diff --git a/server/channelserver/handlers_register.go b/server/channelserver/handlers_register.go index 3d47c3633..895e1c096 100644 --- a/server/channelserver/handlers_register.go +++ b/server/channelserver/handlers_register.go @@ -129,12 +129,12 @@ func (s *Session) notifyRavi() { raviNotif.WriteUint16(0x0010) // End it. if s.server.erupeConfig.GameplayOptions.LowLatencyRaviente { for session := range sema.clients { - session.QueueSend(raviNotif.Data()) + session.QueueSendNonBlocking(raviNotif.Data()) } } else { for session := range sema.clients { if session.charID == s.charID { - session.QueueSend(raviNotif.Data()) + session.QueueSendNonBlocking(raviNotif.Data()) } } } diff --git a/server/channelserver/handlers_semaphore.go b/server/channelserver/handlers_semaphore.go index 19925c6d6..2088cabea 100644 --- a/server/channelserver/handlers_semaphore.go +++ b/server/channelserver/handlers_semaphore.go @@ -65,6 +65,15 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysCreateAcquireSemaphore) SemaphoreID := pkt.SemaphoreID + if s.server.HasSemaphore(s) { + s.semaphoreMode = !s.semaphoreMode + } + if s.semaphoreMode { + s.semaphoreID[1]++ + } else { + s.semaphoreID[0]++ + } + newSemaphore, exists := s.server.semaphore[SemaphoreID] if !exists { s.server.semaphoreLock.Lock() @@ -77,7 +86,7 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { maxPlayers: 127, } } else { - s.server.semaphore[SemaphoreID] = NewSemaphore(s.server, SemaphoreID, 1) + s.server.semaphore[SemaphoreID] = NewSemaphore(s, SemaphoreID, 1) } newSemaphore = s.server.semaphore[SemaphoreID] s.server.semaphoreLock.Unlock() @@ -103,7 +112,7 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysAcquireSemaphore) if sema, exists := s.server.semaphore[pkt.SemaphoreID]; exists { - sema.clients[s] = s.charID + sema.host = s bf := byteframe.NewByteFrame() bf.WriteUint32(sema.id) doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) diff --git a/server/channelserver/handlers_stage.go b/server/channelserver/handlers_stage.go index 8a1abbc35..64a1153ef 100644 --- a/server/channelserver/handlers_stage.go +++ b/server/channelserver/handlers_stage.go @@ -59,7 +59,7 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) { s.Unlock() // Tell the client to cleanup its current stage objects. - s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{}) + s.QueueSendMHFNonBlocking(&mhfpacket.MsgSysCleanupObject{}) // Confirm the stage entry. doAckSimpleSucceed(s, ackHandle, []byte{0x00, 0x00, 0x00, 0x00}) @@ -112,9 +112,8 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) { s.stage.RUnlock() } - newNotif.WriteUint16(0x0010) // End it. if len(newNotif.Data()) > 2 { - s.QueueSend(newNotif.Data()) + s.QueueSendNonBlocking(newNotif.Data()) } } @@ -238,7 +237,7 @@ func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) { for charID := range s.reservationStage.reservedClientSlots { session := s.server.FindSessionByCharID(charID) if session != nil { - session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{}) + session.QueueSendMHFNonBlocking(&mhfpacket.MsgSysStageDestruct{}) } } @@ -362,7 +361,7 @@ func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) { doAckBufSucceed(s, pkt.AckHandle, []byte{0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) return } - for { + for i := 0; i < 10; i++ { s.logger.Debug("MsgSysWaitStageBinary before lock and get stage") stage.Lock() stageBinary, gotBinary := stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}] @@ -370,13 +369,15 @@ func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) { s.logger.Debug("MsgSysWaitStageBinary after lock and get stage") if gotBinary { doAckBufSucceed(s, pkt.AckHandle, stageBinary) - break + return } else { s.logger.Debug("Waiting stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1)) time.Sleep(1 * time.Second) continue } } + s.logger.Warn("MsgSysWaitStageBinary stage binary timeout") + doAckBufSucceed(s, pkt.AckHandle, []byte{}) } else { s.logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID)) } diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index a0d1fe1b7..f62db7e34 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -75,6 +75,7 @@ type Server struct { raviente *Raviente + questCacheLock sync.RWMutex questCacheData map[int][]byte questCacheTime map[int]time.Time } @@ -207,6 +208,7 @@ func (s *Server) Start() error { go s.acceptClients() go s.manageSessions() + go s.invalidateSessions() // Start the discord bot for chat integration. if s.erupeConfig.Discord.Enabled && s.discordBot != nil { @@ -278,6 +280,21 @@ func (s *Server) manageSessions() { } } +func (s *Server) invalidateSessions() { + for { + if s.isShuttingDown { + break + } + for _, sess := range s.sessions { + if time.Now().Sub(sess.lastPacket) > time.Second*time.Duration(30) { + s.logger.Info("session timeout", zap.String("Name", sess.Name)) + logoutPlayer(sess) + } + } + time.Sleep(time.Second * 10) + } +} + // BroadcastMHF queues a MHFPacket to be sent to all sessions. func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) { // Broadcast the data. @@ -424,24 +441,13 @@ func (s *Server) FindObjectByChar(charID uint32) *Object { return nil } -func (s *Server) NextSemaphoreID() uint32 { - for { - exists := false - s.semaphoreIndex = s.semaphoreIndex + 1 - if s.semaphoreIndex > 0xFFFF { - s.semaphoreIndex = 1 - } - for _, semaphore := range s.semaphore { - if semaphore.id == s.semaphoreIndex { - exists = true - break - } - } - if !exists { - break +func (s *Server) HasSemaphore(ses *Session) bool { + for _, semaphore := range s.semaphore { + if semaphore.host == ses { + return true } } - return s.semaphoreIndex + return false } func (s *Server) Season() uint8 { diff --git a/server/channelserver/sys_language.go b/server/channelserver/sys_language.go index aae8706bb..6f24b6f32 100644 --- a/server/channelserver/sys_language.go +++ b/server/channelserver/sys_language.go @@ -10,6 +10,7 @@ type i18n struct { noOp string disabled string reload string + playtime string kqf struct { get string set struct { @@ -175,6 +176,8 @@ func getLangStrings(s *Server) i18n { i.commands.noOp = "You don't have permission to use this command" i.commands.disabled = "%s command is disabled" i.commands.reload = "Reloading players..." + i.commands.playtime = "Playtime: %d hours %d minutes %d seconds" + i.commands.kqf.get = "KQF: %x" i.commands.kqf.set.error = "Error in command. Format: %s set xxxxxxxxxxxxxxxx" i.commands.kqf.set.success = "KQF set, please switch Land/World" diff --git a/server/channelserver/sys_semaphore.go b/server/channelserver/sys_semaphore.go index 78ff963b5..2200877ca 100644 --- a/server/channelserver/sys_semaphore.go +++ b/server/channelserver/sys_semaphore.go @@ -22,15 +22,18 @@ type Semaphore struct { // Max Players for Semaphore maxPlayers uint16 + + host *Session } // NewSemaphore creates a new Semaphore with intialized values -func NewSemaphore(s *Server, ID string, MaxPlayers uint16) *Semaphore { +func NewSemaphore(s *Session, ID string, MaxPlayers uint16) *Semaphore { sema := &Semaphore{ name: ID, - id: s.NextSemaphoreID(), + id: s.GetSemaphoreID(), clients: make(map[*Session]uint32), maxPlayers: MaxPlayers, + host: s, } return sema } diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go index d7e55d621..867c42b04 100644 --- a/server/channelserver/sys_session.go +++ b/server/channelserver/sys_session.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "encoding/hex" "erupe-ce/common/mhfcourse" + _config "erupe-ce/config" "fmt" "io" "net" @@ -15,6 +16,7 @@ import ( "erupe-ce/network" "erupe-ce/network/clientctx" "erupe-ce/network/mhfpacket" + "go.uber.org/zap" ) @@ -48,7 +50,12 @@ type Session struct { kqf []byte kqfOverride bool - semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet. + playtime uint32 + playtimeTime time.Time + + semaphore *Semaphore // Required for the stateful MsgSysUnreserveStage packet. + semaphoreMode bool + semaphoreID []uint16 // A stack containing the stage movement history (push on enter/move, pop on back) stageMoveStack *stringstack.StringStack @@ -79,6 +86,7 @@ func NewSession(server *Server, conn net.Conn) *Session { sessionStart: TimeAdjusted().Unix(), stageMoveStack: stringstack.New(), ackStart: make(map[uint32]time.Time), + semaphoreID: make([]uint16, 2), } s.SetObjectID() return s @@ -96,22 +104,9 @@ func (s *Session) Start() { // QueueSend queues a packet (raw []byte) to be sent. func (s *Session) QueueSend(data []byte) { s.logMessage(binary.BigEndian.Uint16(data[0:2]), data, "Server", s.Name) - select { - case s.sendPackets <- packet{data, false}: - // Enqueued data - default: - s.logger.Warn("Packet queue too full, flushing!") - var tempPackets []packet - for len(s.sendPackets) > 0 { - tempPacket := <-s.sendPackets - if !tempPacket.nonBlocking { - tempPackets = append(tempPackets, tempPacket) - } - } - for _, tempPacket := range tempPackets { - s.sendPackets <- tempPacket - } - s.sendPackets <- packet{data, false} + err := s.cryptConn.SendPacket(append(data, []byte{0x00, 0x10}...)) + if err != nil { + s.logger.Warn("Failed to send packet") } } @@ -138,6 +133,19 @@ func (s *Session) QueueSendMHF(pkt mhfpacket.MHFPacket) { s.QueueSend(bf.Data()) } +// QueueSendMHFNonBlocking queues a MHFPacket to be sent, dropping the packet entirely if the queue is full. +func (s *Session) QueueSendMHFNonBlocking(pkt mhfpacket.MHFPacket) { + // Make the header + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(pkt.Opcode())) + + // Build the packet onto the byteframe. + pkt.Build(bf, s.clientContext) + + // Queue it. + s.QueueSendNonBlocking(bf.Data()) +} + // QueueAck is a helper function to queue an MSG_SYS_ACK with the given ack handle and data. func (s *Session) QueueAck(ackHandle uint32, data []byte) { bf := byteframe.NewByteFrame() @@ -150,26 +158,26 @@ func (s *Session) QueueAck(ackHandle uint32, data []byte) { func (s *Session) sendLoop() { var pkt packet for { + var buf []byte if s.closed { return } for len(s.sendPackets) > 0 { pkt = <-s.sendPackets - err := s.cryptConn.SendPacket(append(pkt.data, []byte{0x00, 0x10}...)) + buf = append(buf, pkt.data...) + } + if len(buf) > 0 { + err := s.cryptConn.SendPacket(append(buf, []byte{0x00, 0x10}...)) if err != nil { s.logger.Warn("Failed to send packet") } } - time.Sleep(5 * time.Millisecond) + time.Sleep(time.Duration(_config.ErupeConfig.LoopDelay) * time.Millisecond) } } func (s *Session) recvLoop() { for { - if time.Now().Add(-30 * time.Second).After(s.lastPacket) { - logoutPlayer(s) - return - } if s.closed { logoutPlayer(s) return @@ -185,7 +193,7 @@ func (s *Session) recvLoop() { return } s.handlePacketGroup(pkt) - time.Sleep(5 * time.Millisecond) + time.Sleep(time.Duration(_config.ErupeConfig.LoopDelay) * time.Millisecond) } } @@ -272,7 +280,7 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien } else { fmt.Printf("[%s] -> [%s]\n", sender, recipient) } - fmt.Printf("Opcode: %s\n", opcodePID) + fmt.Printf("Opcode: (Dec: %d Hex: 0x%04X Name: %s) \n", opcode, opcode, opcodePID) if s.server.erupeConfig.DebugOptions.LogMessageData { if len(data) <= s.server.erupeConfig.DebugOptions.MaxHexdumpLength { fmt.Printf("Data [%d bytes]:\n%s\n", len(data), hex.Dump(data)) @@ -310,6 +318,14 @@ func (s *Session) NextObjectID() uint32 { return bf.ReadUint32() } +func (s *Session) GetSemaphoreID() uint32 { + if s.semaphoreMode { + return 0x000E0000 + uint32(s.semaphoreID[1]) + } else { + return 0x000F0000 + uint32(s.semaphoreID[0]) + } +} + func (s *Session) isOp() bool { var op bool err := s.server.db.QueryRow(`SELECT op FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&op) diff --git a/server/entranceserver/make_resp.go b/server/entranceserver/make_resp.go index ec0281abc..57b04d0e1 100644 --- a/server/entranceserver/make_resp.go +++ b/server/entranceserver/make_resp.go @@ -86,7 +86,7 @@ func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte { } } bf.WriteUint32(uint32(channelserver.TimeAdjusted().Unix())) - bf.WriteUint32(60) + bf.WriteUint32(uint32(s.erupeConfig.GameplayOptions.ClanMemberLimits[len(s.erupeConfig.GameplayOptions.ClanMemberLimits)-1][1])) return bf.Data() }