mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-05-14 10:14:41 +02:00
Merge pull request #26 from ZeruLight/merge/feature/festa
merge changes into feature/festa
This commit is contained in:
25
.github/workflows/go.yml
vendored
25
.github/workflows/go.yml
vendored
@@ -4,7 +4,7 @@ on: [push]
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: windows-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -14,13 +14,28 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: 1.18
|
go-version: 1.18
|
||||||
|
|
||||||
- name: Build
|
- name: Build Linux-amd64
|
||||||
run: go build -v
|
run: env GOOS=linux GOARCH=amd64 go build -v
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload Linux-amd64 artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: Erupe
|
name: Linux-amd64
|
||||||
|
path: |
|
||||||
|
./erupe-ce
|
||||||
|
./config.json
|
||||||
|
./www/
|
||||||
|
./savedata/
|
||||||
|
./bin/
|
||||||
|
./RoadShopItems.csv
|
||||||
|
|
||||||
|
- name: Build Windows-amd64
|
||||||
|
run: env GOOS=windows GOARCH=amd64 go build -v
|
||||||
|
|
||||||
|
- name: Upload Windows-amd64 artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Windows-amd64
|
||||||
path: |
|
path: |
|
||||||
./erupe-ce.exe
|
./erupe-ce.exe
|
||||||
./config.json
|
./config.json
|
||||||
|
|||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2019 The Erupe Developers from Einherjar Team
|
Copyright (c) 2019 The Erupe Developers, The Erupe Developers from Einherjar Team, ZeruLight
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
# Erupe Community Edition
|
# Erupe Community Edition
|
||||||
|
|
||||||
This is a community upload of a community project. The amount of people who worked on it is innumerous, and hard to keep track of. Credits to Andoryuuta, Fist's Team, the French Team, Mai's Team and many others. No matter the relations, these files will remain public and open source, free for all to use and modify.
|
This is a community upload of a community project. The amount of people who worked on it is innumerous, and hard to keep track of. Credits to Andoryuuta, Fist's Team, the French Team, Mai's Team and many others. No matter the relations, these files will remain public and open source, free for all to use and modify.
|
||||||
|
|
||||||
A pastebin with various links, tips, and FAQ: https://pastebin.com/QqAwZSTC
|
[A pastebin with various links, tips, and FAQ](https://pastebin.com/QqAwZSTC)
|
||||||
|
|
||||||
An upload for the quest and scenario files exists here: https://github.com/xl3lackout/MHFZ-Quest-Files
|
[An upload for the quest and scenario files exists here](https://github.com/xl3lackout/MHFZ-Quest-Files)
|
||||||
(Over 300k+ files)
|
(Over 300k+ files)
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ func Uint8(bf *byteframe.ByteFrame, x string, t bool) {
|
|||||||
e := japanese.ShiftJIS.NewEncoder()
|
e := japanese.ShiftJIS.NewEncoder()
|
||||||
xt, _, err := transform.String(e, x)
|
xt, _, err := transform.String(e, x)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
bf.WriteUint8(0)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
x = xt
|
x = xt
|
||||||
}
|
}
|
||||||
@@ -24,7 +25,8 @@ func Uint16(bf *byteframe.ByteFrame, x string, t bool) {
|
|||||||
e := japanese.ShiftJIS.NewEncoder()
|
e := japanese.ShiftJIS.NewEncoder()
|
||||||
xt, _, err := transform.String(e, x)
|
xt, _, err := transform.String(e, x)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
bf.WriteUint16(0)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
x = xt
|
x = xt
|
||||||
}
|
}
|
||||||
@@ -37,7 +39,8 @@ func Uint32(bf *byteframe.ByteFrame, x string, t bool) {
|
|||||||
e := japanese.ShiftJIS.NewEncoder()
|
e := japanese.ShiftJIS.NewEncoder()
|
||||||
xt, _, err := transform.String(e, x)
|
xt, _, err := transform.String(e, x)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
bf.WriteUint32(0)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
x = xt
|
x = xt
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ func PaddedString(x string, size uint, t bool) []byte {
|
|||||||
e := japanese.ShiftJIS.NewEncoder()
|
e := japanese.ShiftJIS.NewEncoder()
|
||||||
xt, _, err := transform.String(e, x)
|
xt, _, err := transform.String(e, x)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return make([]byte, size)
|
||||||
}
|
}
|
||||||
x = xt
|
x = xt
|
||||||
}
|
}
|
||||||
|
|||||||
10
config.json
10
config.json
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"host_ip": "127.0.0.1",
|
"Host": "127.0.0.1",
|
||||||
"bin_path": "bin",
|
"BinPath": "bin",
|
||||||
|
"DisableSoftCrash": false,
|
||||||
"devmode": true,
|
"devmode": true,
|
||||||
"devmodeoptions": {
|
"devmodeoptions": {
|
||||||
"serverName" : "",
|
"serverName" : "",
|
||||||
"hideLoginNotice": false,
|
"hideLoginNotice": false,
|
||||||
"loginNotice": "<BODY><CENTER><SIZE_3><C_4>Welcome to Erupe SU9!<BR><BODY><LEFT><SIZE_2><C_5>Erupe is experimental software<C_7>, we are not liable for any<BR><BODY>issues caused by installing the software!<BR><BODY><BR><BODY><C_4>■Report bugs on Discord!<C_7><BR><BODY><BR><BODY><C_4>■Test everything!<C_7><BR><BODY><BR><BODY><C_4>■Don't talk to softlocking NPCs!<C_7><BR><BODY><BR><BODY><C_4>■Fork the code on GitHub!<C_7><BR><BODY><BR><BODY>Thank you to all of the contributors,<BR><BODY><BR><BODY>this wouldn't exist without you.",
|
"loginNotice": "<BODY><CENTER><SIZE_3><C_4>Welcome to Erupe SU9 (Patch 1)!<BR><BODY><LEFT><SIZE_2><C_5>Erupe is experimental software<C_7>, we are not liable for any<BR><BODY>issues caused by installing the software!<BR><BODY><BR><BODY><C_4>■Report bugs on Discord!<C_7><BR><BODY><BR><BODY><C_4>■Test everything!<C_7><BR><BODY><BR><BODY><C_4>■Don't talk to softlocking NPCs!<C_7><BR><BODY><BR><BODY><C_4>■Fork the code on GitHub!<C_7><BR><BODY><BR><BODY>Thank you to all of the contributors,<BR><BODY><BR><BODY>this wouldn't exist without you.",
|
||||||
"cleandb": false,
|
"cleandb": false,
|
||||||
"maxlauncherhr": false,
|
"maxlauncherhr": false,
|
||||||
"LogInboundMessages": false,
|
"LogInboundMessages": false,
|
||||||
@@ -16,6 +17,9 @@
|
|||||||
"FestaEvent": -1,
|
"FestaEvent": -1,
|
||||||
"TournamentEvent": 0,
|
"TournamentEvent": 0,
|
||||||
"MezFesEvent": true,
|
"MezFesEvent": true,
|
||||||
|
"MezFesAlt": false,
|
||||||
|
"DisableMailItems": true,
|
||||||
|
"DisableTokenCheck": false,
|
||||||
"SaveDumps": {
|
"SaveDumps": {
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"OutputDir": "savedata"
|
"OutputDir": "savedata"
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import (
|
|||||||
|
|
||||||
// Config holds the global server-wide config.
|
// Config holds the global server-wide config.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
HostIP string `mapstructure:"host_ip"`
|
Host string `mapstructure:"Host"`
|
||||||
BinPath string `mapstructure:"bin_path"`
|
BinPath string `mapstructure:"BinPath"`
|
||||||
DevMode bool
|
DisableSoftCrash bool // Disables the 'Press Return to exit' dialog allowing scripts to reboot the server automatically
|
||||||
|
DevMode bool
|
||||||
|
|
||||||
DevModeOptions DevModeOptions
|
DevModeOptions DevModeOptions
|
||||||
Discord Discord
|
Discord Discord
|
||||||
@@ -36,6 +37,9 @@ type DevModeOptions struct {
|
|||||||
FestaEvent int // Hunter's Festa event status
|
FestaEvent int // Hunter's Festa event status
|
||||||
TournamentEvent int // VS Tournament event status
|
TournamentEvent int // VS Tournament event status
|
||||||
MezFesEvent bool // MezFes status
|
MezFesEvent bool // MezFes status
|
||||||
|
MezFesAlt bool // Swaps out Volpakkun for Tokotoko
|
||||||
|
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
|
||||||
|
DisableMailItems bool // Hack to prevent english versions of MHF from crashing
|
||||||
SaveDumps SaveDumpOptions
|
SaveDumps SaveDumpOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,8 +141,8 @@ func LoadConfig() (*Config, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.HostIP == "" {
|
if c.Host == "" {
|
||||||
c.HostIP = getOutboundIP4().To4().String()
|
c.Host = getOutboundIP4().To4().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
|
|||||||
36
main.go
36
main.go
@@ -2,7 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -19,6 +19,8 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var erupeConfig *config.Config
|
||||||
|
|
||||||
// Temporary DB auto clean on startup for quick development & testing.
|
// Temporary DB auto clean on startup for quick development & testing.
|
||||||
func cleanDB(db *sqlx.DB) {
|
func cleanDB(db *sqlx.DB) {
|
||||||
_ = db.MustExec("DELETE FROM guild_characters")
|
_ = db.MustExec("DELETE FROM guild_characters")
|
||||||
@@ -29,6 +31,7 @@ func cleanDB(db *sqlx.DB) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
var err error
|
||||||
zapLogger, _ := zap.NewDevelopment()
|
zapLogger, _ := zap.NewDevelopment()
|
||||||
defer zapLogger.Sync()
|
defer zapLogger.Sync()
|
||||||
logger := zapLogger.Named("main")
|
logger := zapLogger.Named("main")
|
||||||
@@ -36,7 +39,7 @@ func main() {
|
|||||||
logger.Info("Starting Erupe")
|
logger.Info("Starting Erupe")
|
||||||
|
|
||||||
// Load the configuration.
|
// Load the configuration.
|
||||||
erupeConfig, err := config.LoadConfig()
|
erupeConfig, err = config.LoadConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
preventClose(fmt.Sprintf("Failed to load config: %s", err.Error()))
|
preventClose(fmt.Sprintf("Failed to load config: %s", err.Error()))
|
||||||
}
|
}
|
||||||
@@ -45,6 +48,19 @@ func main() {
|
|||||||
preventClose("Database password is blank")
|
preventClose("Database password is blank")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if net.ParseIP(erupeConfig.Host) == nil {
|
||||||
|
ips, _ := net.LookupIP(erupeConfig.Host)
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip != nil {
|
||||||
|
erupeConfig.Host = ip.String()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if net.ParseIP(erupeConfig.Host) == nil {
|
||||||
|
preventClose("Invalid host address")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Discord bot
|
// Discord bot
|
||||||
var discordBot *discordbot.DiscordBot = nil
|
var discordBot *discordbot.DiscordBot = nil
|
||||||
|
|
||||||
@@ -151,9 +167,6 @@ func main() {
|
|||||||
ci := 0
|
ci := 0
|
||||||
count := 1
|
count := 1
|
||||||
for _, ee := range erupeConfig.Entrance.Entries {
|
for _, ee := range erupeConfig.Entrance.Entries {
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
// Randomly generate a season for the World
|
|
||||||
season := rand.Intn(3) + 1
|
|
||||||
for _, ce := range ee.Channels {
|
for _, ce := range ee.Channels {
|
||||||
sid := (4096 + si*256) + (16 + ci)
|
sid := (4096 + si*256) + (16 + ci)
|
||||||
c := *channelserver.NewServer(&channelserver.Config{
|
c := *channelserver.NewServer(&channelserver.Config{
|
||||||
@@ -163,11 +176,17 @@ func main() {
|
|||||||
DB: db,
|
DB: db,
|
||||||
DiscordBot: discordBot,
|
DiscordBot: discordBot,
|
||||||
})
|
})
|
||||||
err = c.Start(int(ce.Port))
|
if ee.IP == "" {
|
||||||
|
c.IP = erupeConfig.Host
|
||||||
|
} else {
|
||||||
|
c.IP = ee.IP
|
||||||
|
}
|
||||||
|
c.Port = ce.Port
|
||||||
|
err = c.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error()))
|
preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error()))
|
||||||
} else {
|
} else {
|
||||||
channelQuery += fmt.Sprintf("INSERT INTO servers (server_id, season, current_players) VALUES (%d, %d, 0);", sid, season)
|
channelQuery += fmt.Sprintf("INSERT INTO servers (server_id, season, current_players) VALUES (%d, %d, 0);", sid, si%3)
|
||||||
channels = append(channels, &c)
|
channels = append(channels, &c)
|
||||||
logger.Info(fmt.Sprintf("Started channel server %d on port %d", count, ce.Port))
|
logger.Info(fmt.Sprintf("Started channel server %d on port %d", count, ce.Port))
|
||||||
ci++
|
ci++
|
||||||
@@ -209,6 +228,9 @@ func wait() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func preventClose(text string) {
|
func preventClose(text string) {
|
||||||
|
if erupeConfig.DisableSoftCrash {
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
fmt.Println("\nFailed to start Erupe:\n" + text)
|
fmt.Println("\nFailed to start Erupe:\n" + text)
|
||||||
go wait()
|
go wait()
|
||||||
fmt.Println("\nPress Enter/Return to exit...")
|
fmt.Println("\nPress Enter/Return to exit...")
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfAcquireTitle represents the MSG_MHF_ACQUIRE_TITLE
|
// MsgMhfAcquireTitle represents the MSG_MHF_ACQUIRE_TITLE
|
||||||
type MsgMhfAcquireTitle struct{}
|
type MsgMhfAcquireTitle struct {
|
||||||
|
AckHandle uint32
|
||||||
|
Unk0 uint16
|
||||||
|
Unk1 uint16
|
||||||
|
TitleID uint16
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfAcquireTitle) Opcode() network.PacketID {
|
func (m *MsgMhfAcquireTitle) Opcode() network.PacketID {
|
||||||
@@ -18,7 +23,11 @@ func (m *MsgMhfAcquireTitle) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfAcquireTitle) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfAcquireTitle) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
m.Unk0 = bf.ReadUint16()
|
||||||
|
m.Unk1 = bf.ReadUint16()
|
||||||
|
m.TitleID = bf.ReadUint16()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package mhfpacket
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/network"
|
"erupe-ce/network"
|
||||||
@@ -30,6 +31,7 @@ const (
|
|||||||
type MsgMhfEnumerateGuild struct {
|
type MsgMhfEnumerateGuild struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Type EnumerateGuildType
|
Type EnumerateGuildType
|
||||||
|
Page uint8
|
||||||
RawDataPayload []byte
|
RawDataPayload []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,8 +44,9 @@ func (m *MsgMhfEnumerateGuild) Opcode() network.PacketID {
|
|||||||
func (m *MsgMhfEnumerateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfEnumerateGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Type = EnumerateGuildType(bf.ReadUint8())
|
m.Type = EnumerateGuildType(bf.ReadUint8())
|
||||||
|
m.Page = bf.ReadUint8()
|
||||||
m.RawDataPayload = bf.DataFromCurrent()
|
m.RawDataPayload = bf.DataFromCurrent()
|
||||||
bf.Seek(int64(len(bf.Data())-2), 0)
|
bf.Seek(-2, io.SeekEnd)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfEnumerateInvGuild represents the MSG_MHF_ENUMERATE_INV_GUILD
|
// MsgMhfEnumerateInvGuild represents the MSG_MHF_ENUMERATE_INV_GUILD
|
||||||
type MsgMhfEnumerateInvGuild struct{}
|
type MsgMhfEnumerateInvGuild struct {
|
||||||
|
AckHandle uint32
|
||||||
|
Unk []byte
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfEnumerateInvGuild) Opcode() network.PacketID {
|
func (m *MsgMhfEnumerateInvGuild) Opcode() network.PacketID {
|
||||||
@@ -18,7 +21,9 @@ func (m *MsgMhfEnumerateInvGuild) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfEnumerateInvGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfEnumerateInvGuild) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
m.Unk = bf.ReadBytes(9)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfGetAchievement represents the MSG_MHF_GET_ACHIEVEMENT
|
// MsgMhfGetAchievement represents the MSG_MHF_GET_ACHIEVEMENT
|
||||||
type MsgMhfGetAchievement struct{
|
type MsgMhfGetAchievement struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Unk0 uint32 // id?
|
CharID uint32
|
||||||
Unk1 uint32 // char?
|
Unk1 uint32 // char?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -22,8 +22,8 @@ func (m *MsgMhfGetAchievement) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfGetAchievement) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfGetAchievement) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Unk0 = bf.ReadUint32()
|
m.CharID = bf.ReadUint32()
|
||||||
m.Unk1 = bf.ReadUint32()
|
m.Unk1 = bf.ReadUint32()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OperateGuildMemberAction uint8
|
type OperateGuildMemberAction uint8
|
||||||
@@ -23,6 +23,7 @@ type MsgMhfOperateGuildMember struct {
|
|||||||
GuildID uint32
|
GuildID uint32
|
||||||
CharID uint32
|
CharID uint32
|
||||||
Action uint8
|
Action uint8
|
||||||
|
Unk []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -36,7 +37,7 @@ func (m *MsgMhfOperateGuildMember) Parse(bf *byteframe.ByteFrame, ctx *clientctx
|
|||||||
m.GuildID = bf.ReadUint32()
|
m.GuildID = bf.ReadUint32()
|
||||||
m.CharID = bf.ReadUint32()
|
m.CharID = bf.ReadUint32()
|
||||||
m.Action = bf.ReadUint8()
|
m.Action = bf.ReadUint8()
|
||||||
|
m.Unk = bf.ReadBytes(3)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OperateMailOperation uint8
|
type OperateMailOperation uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
OPERATE_MAIL_DELETE = 0x01
|
OPERATE_MAIL_DELETE = 0x01
|
||||||
OPERATE_MAIL_LOCK = 0x02
|
OPERATE_MAIL_LOCK = 0x02
|
||||||
OPERATE_MAIL_UNLOCK = 0x03
|
OPERATE_MAIL_UNLOCK = 0x03
|
||||||
OPERATE_MAIL_ACQUIRE_ITEM = 0x05
|
OPERATE_MAIL_ACQUIRE_ITEM = 0x05
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfOprtMail represents the MSG_MHF_OPRT_MAIL
|
// MsgMhfOprtMail represents the MSG_MHF_OPRT_MAIL
|
||||||
@@ -23,10 +23,10 @@ type MsgMhfOprtMail struct {
|
|||||||
AccIndex uint8
|
AccIndex uint8
|
||||||
Index uint8
|
Index uint8
|
||||||
Operation OperateMailOperation
|
Operation OperateMailOperation
|
||||||
Unk0 uint8
|
Unk0 uint8
|
||||||
Data []byte
|
Data []byte
|
||||||
Amount uint16
|
Amount uint16
|
||||||
ItemID uint16
|
ItemID uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -40,12 +40,11 @@ func (m *MsgMhfOprtMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientCon
|
|||||||
m.AccIndex = bf.ReadUint8()
|
m.AccIndex = bf.ReadUint8()
|
||||||
m.Index = bf.ReadUint8()
|
m.Index = bf.ReadUint8()
|
||||||
m.Operation = OperateMailOperation(bf.ReadUint8())
|
m.Operation = OperateMailOperation(bf.ReadUint8())
|
||||||
m.Unk0 = bf.ReadUint8()
|
m.Unk0 = bf.ReadUint8()
|
||||||
switch m.Operation {
|
if m.Operation == OPERATE_MAIL_ACQUIRE_ITEM {
|
||||||
case OPERATE_MAIL_ACQUIRE_ITEM:
|
m.Amount = bf.ReadUint16()
|
||||||
m.Amount = bf.ReadUint16()
|
m.ItemID = bf.ReadUint16()
|
||||||
m.ItemID = bf.ReadUint16()
|
}
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfReadMercenaryW represents the MSG_MHF_READ_MERCENARY_W
|
// MsgMhfReadMercenaryW represents the MSG_MHF_READ_MERCENARY_W
|
||||||
type MsgMhfReadMercenaryW struct {
|
type MsgMhfReadMercenaryW struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Unk0 uint8
|
Unk0 bool
|
||||||
Unk1 uint8
|
Unk1 uint8
|
||||||
Unk2 uint16 // Hardcoded 0 in the binary
|
Unk2 uint16 // Hardcoded 0 in the binary
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ func (m *MsgMhfReadMercenaryW) Opcode() network.PacketID {
|
|||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfReadMercenaryW) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfReadMercenaryW) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Unk0 = bf.ReadUint8()
|
m.Unk0 = bf.ReadBool()
|
||||||
m.Unk1 = bf.ReadUint8()
|
m.Unk1 = bf.ReadUint8()
|
||||||
m.Unk2 = bf.ReadUint16()
|
m.Unk2 = bf.ReadUint16()
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfSaveMercenary represents the MSG_MHF_SAVE_MERCENARY
|
// MsgMhfSaveMercenary represents the MSG_MHF_SAVE_MERCENARY
|
||||||
type MsgMhfSaveMercenary struct{
|
type MsgMhfSaveMercenary struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
DataSize uint32
|
GCP uint32
|
||||||
RawDataPayload []byte
|
Unk0 uint32
|
||||||
|
MercData []byte
|
||||||
|
Unk1 uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -23,8 +25,11 @@ func (m *MsgMhfSaveMercenary) Opcode() network.PacketID {
|
|||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfSaveMercenary) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfSaveMercenary) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.DataSize = bf.ReadUint32()
|
bf.ReadUint32() // lenData
|
||||||
m.RawDataPayload = bf.ReadBytes(uint(m.DataSize))
|
m.GCP = bf.ReadUint32()
|
||||||
|
m.Unk0 = bf.ReadUint32()
|
||||||
|
m.MercData = bf.ReadBytes(uint(bf.ReadUint32()))
|
||||||
|
m.Unk1 = bf.ReadUint32()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"erupe-ce/common/stringsupport"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfSendMail represents the MSG_MHF_SEND_MAIL
|
// MsgMhfSendMail represents the MSG_MHF_SEND_MAIL
|
||||||
type MsgMhfSendMail struct {
|
type MsgMhfSendMail struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
RecipientID uint32
|
RecipientID uint32
|
||||||
SubjectLength uint16
|
SubjectLength uint16
|
||||||
BodyLength uint16
|
BodyLength uint16
|
||||||
Quantity uint32
|
Quantity uint32
|
||||||
ItemID uint16
|
ItemID uint16
|
||||||
Subject []byte
|
Subject string
|
||||||
Body []byte
|
Body string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -27,15 +28,15 @@ func (m *MsgMhfSendMail) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfSendMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfSendMail) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.RecipientID = bf.ReadUint32()
|
m.RecipientID = bf.ReadUint32()
|
||||||
m.SubjectLength = bf.ReadUint16()
|
m.SubjectLength = bf.ReadUint16()
|
||||||
m.BodyLength = bf.ReadUint16()
|
m.BodyLength = bf.ReadUint16()
|
||||||
m.Quantity = bf.ReadUint32()
|
m.Quantity = bf.ReadUint32()
|
||||||
m.ItemID = bf.ReadUint16()
|
m.ItemID = bf.ReadUint16()
|
||||||
m.Subject = bf.ReadNullTerminatedBytes()
|
m.Subject = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
|
||||||
m.Body = bf.ReadNullTerminatedBytes()
|
m.Body = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfSetGuildManageRight represents the MSG_MHF_SET_GUILD_MANAGE_RIGHT
|
// MsgMhfSetGuildManageRight represents the MSG_MHF_SET_GUILD_MANAGE_RIGHT
|
||||||
type MsgMhfSetGuildManageRight struct{}
|
type MsgMhfSetGuildManageRight struct {
|
||||||
|
AckHandle uint32
|
||||||
|
CharID uint32
|
||||||
|
Allowed bool
|
||||||
|
Unk []byte
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgMhfSetGuildManageRight) Opcode() network.PacketID {
|
func (m *MsgMhfSetGuildManageRight) Opcode() network.PacketID {
|
||||||
@@ -18,7 +23,11 @@ func (m *MsgMhfSetGuildManageRight) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfSetGuildManageRight) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfSetGuildManageRight) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
m.CharID = bf.ReadUint32()
|
||||||
|
m.Allowed = bf.ReadBool()
|
||||||
|
m.Unk = bf.ReadBytes(3)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgMhfTransitMessage represents the MSG_MHF_TRANSIT_MESSAGE
|
// MsgMhfTransitMessage represents the MSG_MHF_TRANSIT_MESSAGE
|
||||||
type MsgMhfTransitMessage struct {
|
type MsgMhfTransitMessage struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Unk0 uint8
|
Unk0 uint8
|
||||||
Unk1 uint8
|
Unk1 uint8
|
||||||
Unk2 uint16
|
SearchType uint16
|
||||||
MessageData []byte
|
MessageData []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -24,12 +24,12 @@ func (m *MsgMhfTransitMessage) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgMhfTransitMessage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgMhfTransitMessage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Unk0 = bf.ReadUint8()
|
m.Unk0 = bf.ReadUint8()
|
||||||
m.Unk1 = bf.ReadUint8()
|
m.Unk1 = bf.ReadUint8()
|
||||||
m.Unk2 = bf.ReadUint16()
|
m.SearchType = bf.ReadUint16()
|
||||||
m.MessageData = bf.ReadBytes(uint(bf.ReadUint16()))
|
m.MessageData = bf.ReadBytes(uint(bf.ReadUint16()))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"erupe-ce/common/bfutil"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgSysAcquireSemaphore represents the MSG_SYS_ACQUIRE_SEMAPHORE
|
// MsgSysAcquireSemaphore represents the MSG_SYS_ACQUIRE_SEMAPHORE
|
||||||
type MsgSysAcquireSemaphore struct{}
|
type MsgSysAcquireSemaphore struct {
|
||||||
|
AckHandle uint32
|
||||||
|
SemaphoreID string
|
||||||
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
func (m *MsgSysAcquireSemaphore) Opcode() network.PacketID {
|
func (m *MsgSysAcquireSemaphore) Opcode() network.PacketID {
|
||||||
@@ -18,7 +22,10 @@ func (m *MsgSysAcquireSemaphore) Opcode() network.PacketID {
|
|||||||
|
|
||||||
// Parse parses the packet from binary
|
// Parse parses the packet from binary
|
||||||
func (m *MsgSysAcquireSemaphore) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgSysAcquireSemaphore) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
return errors.New("NOT IMPLEMENTED")
|
m.AckHandle = bf.ReadUint32()
|
||||||
|
SemaphoreIDLength := bf.ReadUint8()
|
||||||
|
m.SemaphoreID = string(bfutil.UpToNull(bf.ReadBytes(uint(SemaphoreIDLength))))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds a binary packet from the current data.
|
// Build builds a binary packet from the current data.
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package mhfpacket
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"erupe-ce/common/byteframe"
|
|
||||||
"erupe-ce/common/bfutil"
|
"erupe-ce/common/bfutil"
|
||||||
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/network"
|
"erupe-ce/network"
|
||||||
"erupe-ce/network/clientctx"
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
type MsgSysEnumerateClient struct {
|
type MsgSysEnumerateClient struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Unk0 uint8 // Hardcoded 1 in the client
|
Unk0 uint8 // Hardcoded 1 in the client
|
||||||
Unk1 uint8
|
Get uint8
|
||||||
StageID string
|
StageID string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ func (m *MsgSysEnumerateClient) Opcode() network.PacketID {
|
|||||||
func (m *MsgSysEnumerateClient) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgSysEnumerateClient) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Unk0 = bf.ReadUint8()
|
m.Unk0 = bf.ReadUint8()
|
||||||
m.Unk1 = bf.ReadUint8()
|
m.Get = bf.ReadUint8()
|
||||||
stageIDLength := bf.ReadUint8()
|
stageIDLength := bf.ReadUint8()
|
||||||
m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength))))
|
m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength))))
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
package mhfpacket
|
package mhfpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"erupe-ce/common/stringsupport"
|
||||||
|
|
||||||
"erupe-ce/network/clientctx"
|
|
||||||
"erupe-ce/network"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network"
|
||||||
|
"erupe-ce/network/clientctx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MsgSysEnumerateStage represents the MSG_SYS_ENUMERATE_STAGE
|
// MsgSysEnumerateStage represents the MSG_SYS_ENUMERATE_STAGE
|
||||||
type MsgSysEnumerateStage struct {
|
type MsgSysEnumerateStage struct {
|
||||||
AckHandle uint32
|
AckHandle uint32
|
||||||
Unk0 uint8 // Hardcoded 1 in the binary
|
Unk0 uint8 // Hardcoded 1 in the binary
|
||||||
StageIDLength uint8
|
StagePrefix string // NULL terminated string.
|
||||||
StageID string // NULL terminated string.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Opcode returns the ID associated with this packet type.
|
// Opcode returns the ID associated with this packet type.
|
||||||
@@ -25,8 +25,8 @@ func (m *MsgSysEnumerateStage) Opcode() network.PacketID {
|
|||||||
func (m *MsgSysEnumerateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
func (m *MsgSysEnumerateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
|
||||||
m.AckHandle = bf.ReadUint32()
|
m.AckHandle = bf.ReadUint32()
|
||||||
m.Unk0 = bf.ReadUint8()
|
m.Unk0 = bf.ReadUint8()
|
||||||
m.StageIDLength = bf.ReadUint8()
|
bf.ReadUint8()
|
||||||
m.StageID = string(bf.ReadBytes(uint(m.StageIDLength)))
|
m.StagePrefix = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
0
patch-schema/.gitkeep
Normal file
0
patch-schema/.gitkeep
Normal file
41
patch-schema/achievements.sql
Normal file
41
patch-schema/achievements.sql
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.achievements
|
||||||
|
(
|
||||||
|
id int NOT NULL PRIMARY KEY ,
|
||||||
|
ach0 int DEFAULT 0,
|
||||||
|
ach1 int DEFAULT 0,
|
||||||
|
ach2 int DEFAULT 0,
|
||||||
|
ach3 int DEFAULT 0,
|
||||||
|
ach4 int DEFAULT 0,
|
||||||
|
ach5 int DEFAULT 0,
|
||||||
|
ach6 int DEFAULT 0,
|
||||||
|
ach7 int DEFAULT 0,
|
||||||
|
ach8 int DEFAULT 0,
|
||||||
|
ach9 int DEFAULT 0,
|
||||||
|
ach10 int DEFAULT 0,
|
||||||
|
ach11 int DEFAULT 0,
|
||||||
|
ach12 int DEFAULT 0,
|
||||||
|
ach13 int DEFAULT 0,
|
||||||
|
ach14 int DEFAULT 0,
|
||||||
|
ach15 int DEFAULT 0,
|
||||||
|
ach16 int DEFAULT 0,
|
||||||
|
ach17 int DEFAULT 0,
|
||||||
|
ach18 int DEFAULT 0,
|
||||||
|
ach19 int DEFAULT 0,
|
||||||
|
ach20 int DEFAULT 0,
|
||||||
|
ach21 int DEFAULT 0,
|
||||||
|
ach22 int DEFAULT 0,
|
||||||
|
ach23 int DEFAULT 0,
|
||||||
|
ach24 int DEFAULT 0,
|
||||||
|
ach25 int DEFAULT 0,
|
||||||
|
ach26 int DEFAULT 0,
|
||||||
|
ach27 int DEFAULT 0,
|
||||||
|
ach28 int DEFAULT 0,
|
||||||
|
ach29 int DEFAULT 0,
|
||||||
|
ach30 int DEFAULT 0,
|
||||||
|
ach31 int DEFAULT 0,
|
||||||
|
ach32 int DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
END;
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.users
|
|
||||||
ALTER rights SET DEFAULT 14;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.users
|
|
||||||
ALTER rights SET NOT NULL;
|
|
||||||
|
|
||||||
UPDATE public.users SET rights=14 WHERE rights IS NULL;
|
|
||||||
|
|
||||||
END;
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
CREATE SEQUENCE IF NOT EXISTS public.airou_id_seq;
|
|
||||||
|
|
||||||
END;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
CREATE TABLE user_binaries
|
|
||||||
(
|
|
||||||
id int PRIMARY KEY,
|
|
||||||
type2 bytea,
|
|
||||||
type3 bytea
|
|
||||||
);
|
|
||||||
|
|
||||||
END;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
CREATE TABLE IF NOT EXISTS public.distribution
|
|
||||||
(
|
|
||||||
id serial NOT NULL PRIMARY KEY,
|
|
||||||
character_id int,
|
|
||||||
type int NOT NULL,
|
|
||||||
deadline timestamp without time zone,
|
|
||||||
event_name text NOT NULL DEFAULT 'GM Gift!',
|
|
||||||
description text NOT NULL DEFAULT '~C05You received a gift!',
|
|
||||||
times_acceptable int NOT NULL DEFAULT 1,
|
|
||||||
min_hr int NOT NULL DEFAULT 65535,
|
|
||||||
max_hr int NOT NULL DEFAULT 65535,
|
|
||||||
min_sr int NOT NULL DEFAULT 65535,
|
|
||||||
max_sr int NOT NULL DEFAULT 65535,
|
|
||||||
min_gr int NOT NULL DEFAULT 65535,
|
|
||||||
max_gr int NOT NULL DEFAULT 65535,
|
|
||||||
data bytea NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.distributions_accepted
|
|
||||||
(
|
|
||||||
distribution_id int,
|
|
||||||
character_id int
|
|
||||||
);
|
|
||||||
|
|
||||||
END;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.gook
|
|
||||||
DROP COLUMN IF EXISTS gook0status;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.gook
|
|
||||||
DROP COLUMN IF EXISTS gook1status;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.gook
|
|
||||||
DROP COLUMN IF EXISTS gook2status;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.gook
|
|
||||||
DROP COLUMN IF EXISTS gook3status;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.gook
|
|
||||||
DROP COLUMN IF EXISTS gook4status;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.gook
|
|
||||||
DROP COLUMN IF EXISTS gook5status;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.gook
|
|
||||||
DROP COLUMN IF EXISTS gook5;
|
|
||||||
|
|
||||||
UPDATE public.gook SET gook0=NULL, gook1=NULL, gook2=NULL, gook3=NULL, gook4=NULL;
|
|
||||||
|
|
||||||
END;
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.guilds
|
|
||||||
ADD COLUMN IF NOT EXISTS pugi_name_1 varchar(12) DEFAULT '';
|
|
||||||
ALTER TABLE IF EXISTS public.guilds
|
|
||||||
ADD COLUMN IF NOT EXISTS pugi_name_2 varchar(12) DEFAULT '';
|
|
||||||
ALTER TABLE IF EXISTS public.guilds
|
|
||||||
ADD COLUMN IF NOT EXISTS pugi_name_3 varchar(12) DEFAULT '';
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.guild_alliances
|
|
||||||
(
|
|
||||||
id serial NOT NULL PRIMARY KEY,
|
|
||||||
name varchar(24) NOT NULL,
|
|
||||||
created_at timestamp without time zone NOT NULL DEFAULT now(),
|
|
||||||
parent_id int NOT NULL,
|
|
||||||
sub1_id int,
|
|
||||||
sub2_id int
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.guild_adventures
|
|
||||||
(
|
|
||||||
id serial NOT NULL PRIMARY KEY,
|
|
||||||
guild_id int NOT NULL,
|
|
||||||
destination int NOT NULL,
|
|
||||||
charge int NOT NULL DEFAULT 0,
|
|
||||||
depart int NOT NULL,
|
|
||||||
return int NOT NULL,
|
|
||||||
collected_by text NOT NULL DEFAULT ''
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.guild_meals
|
|
||||||
(
|
|
||||||
id serial NOT NULL PRIMARY KEY,
|
|
||||||
guild_id int NOT NULL,
|
|
||||||
meal_id int NOT NULL,
|
|
||||||
level int NOT NULL,
|
|
||||||
expires int NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.guild_hunts
|
|
||||||
(
|
|
||||||
id serial NOT NULL PRIMARY KEY,
|
|
||||||
guild_id int NOT NULL,
|
|
||||||
host_id int NOT NULL,
|
|
||||||
destination int NOT NULL,
|
|
||||||
level int NOT NULL,
|
|
||||||
return int NOT NULL,
|
|
||||||
acquired bool NOT NULL DEFAULT false,
|
|
||||||
claimed bool NOT NULL DEFAULT false,
|
|
||||||
hunters text NOT NULL DEFAULT '',
|
|
||||||
treasure text NOT NULL DEFAULT '',
|
|
||||||
hunt_data bytea NOT NULL,
|
|
||||||
cats_used text NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
END;
|
|
||||||
9
patch-schema/guild-enumeration.sql
Normal file
9
patch-schema/guild-enumeration.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.guilds
|
||||||
|
ADD COLUMN IF NOT EXISTS recruiting bool NOT NULL DEFAULT true;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.guild_characters
|
||||||
|
ADD COLUMN IF NOT EXISTS recruiter bool NOT NULL DEFAULT false;
|
||||||
|
|
||||||
|
END;
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.characters
|
|
||||||
ADD COLUMN IF NOT EXISTS house bytea;
|
|
||||||
|
|
||||||
END;
|
|
||||||
5
patch-schema/mercenary.sql
Normal file
5
patch-schema/mercenary.sql
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE SEQUENCE IF NOT EXISTS public.rasta_id_seq;
|
||||||
|
|
||||||
|
END;
|
||||||
6
patch-schema/missing-mail.sql
Normal file
6
patch-schema/missing-mail.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.mail
|
||||||
|
ADD COLUMN IF NOT EXISTS locked boolean NOT NULL DEFAULT false;
|
||||||
|
|
||||||
|
END;
|
||||||
9
patch-schema/revert-road-shop.sql
Normal file
9
patch-schema/revert-road-shop.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.normal_shop_items
|
||||||
|
DROP COLUMN IF EXISTS enable_weeks;
|
||||||
|
|
||||||
|
ALTER TABLE IF EXISTS public.shop_item_state
|
||||||
|
DROP COLUMN IF EXISTS week;
|
||||||
|
|
||||||
|
END;
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
CREATE TABLE IF NOT EXISTS public.normal_shop_items
|
|
||||||
(
|
|
||||||
shoptype integer,
|
|
||||||
shopid integer,
|
|
||||||
itemhash integer not null,
|
|
||||||
itemid integer,
|
|
||||||
points integer,
|
|
||||||
tradequantity integer,
|
|
||||||
rankreqlow integer,
|
|
||||||
rankreqhigh integer,
|
|
||||||
rankreqg integer,
|
|
||||||
storelevelreq integer,
|
|
||||||
maximumquantity integer,
|
|
||||||
boughtquantity integer,
|
|
||||||
roadfloorsrequired integer,
|
|
||||||
weeklyfataliskills integer,
|
|
||||||
enable_weeks character varying(8)
|
|
||||||
);
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.normal_shop_items
|
|
||||||
ADD COLUMN IF NOT EXISTS enable_weeks character varying(8);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS public.shop_item_state
|
|
||||||
(
|
|
||||||
char_id bigint REFERENCES characters (id),
|
|
||||||
itemhash int UNIQUE NOT NULL,
|
|
||||||
usedquantity int,
|
|
||||||
week int
|
|
||||||
);
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.shop_item_state
|
|
||||||
ADD COLUMN IF NOT EXISTS week int;
|
|
||||||
|
|
||||||
END;
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.characters
|
|
||||||
ADD COLUMN IF NOT EXISTS scenariodata bytea;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.characters
|
|
||||||
ADD COLUMN IF NOT EXISTS savefavoritequest bytea;
|
|
||||||
|
|
||||||
END;
|
|
||||||
11
patch-schema/titles.sql
Normal file
11
patch-schema/titles.sql
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS public.titles
|
||||||
|
(
|
||||||
|
id int NOT NULL,
|
||||||
|
char_id int NOT NULL,
|
||||||
|
unlocked_at timestamp without time zone,
|
||||||
|
updated_at timestamp without time zone
|
||||||
|
);
|
||||||
|
|
||||||
|
END;
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS public.sign_sessions;
|
|
||||||
CREATE TABLE IF NOT EXISTS public.sign_sessions
|
|
||||||
(
|
|
||||||
user_id int NOT NULL,
|
|
||||||
char_id int,
|
|
||||||
token varchar(16) NOT NULL,
|
|
||||||
server_id integer
|
|
||||||
);
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS public.servers;
|
|
||||||
CREATE TABLE IF NOT EXISTS public.servers
|
|
||||||
(
|
|
||||||
server_id int NOT NULL,
|
|
||||||
season int NOT NULL,
|
|
||||||
current_players int NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.characters
|
|
||||||
ADD COLUMN IF NOT EXISTS deleted boolean NOT NULL DEFAULT false;
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.characters
|
|
||||||
ADD COLUMN IF NOT EXISTS friends text NOT NULL DEFAULT '';
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.characters
|
|
||||||
ADD COLUMN IF NOT EXISTS blocked text NOT NULL DEFAULT '';
|
|
||||||
|
|
||||||
ALTER TABLE IF EXISTS public.users
|
|
||||||
ADD COLUMN IF NOT EXISTS last_character int DEFAULT 0;
|
|
||||||
|
|
||||||
END;
|
|
||||||
@@ -4,7 +4,11 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"erupe-ce/common/stringsupport"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
@@ -135,20 +139,19 @@ func handleMsgSysTerminalLog(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysLogin)
|
pkt := p.(*mhfpacket.MsgSysLogin)
|
||||||
|
|
||||||
rights := uint32(0x0E)
|
if !s.server.erupeConfig.DevModeOptions.DisableTokenCheck {
|
||||||
// 0e with normal sub 4e when having premium
|
var token string
|
||||||
// 01 = Character can take quests at allows
|
err := s.server.db.QueryRow("SELECT token FROM sign_sessions WHERE token=$1", pkt.LoginTokenString).Scan(&token)
|
||||||
// 02 = Hunter Life, normal quests core sub
|
if err != nil {
|
||||||
// 03 = Extra Course, extra quests, town boxes, QOL course, core sub
|
s.rawConn.Close()
|
||||||
// 06 = Premium Course, standard 'premium' which makes ranking etc. faster
|
s.logger.Warn(fmt.Sprintf("Invalid login token, offending CID: (%d)", pkt.CharID0))
|
||||||
// 06 0A 0B = Boost Course, just actually 3 subs combined
|
return
|
||||||
// 08 09 1E = N Course, gives you the benefits of being in a netcafe (extra quests, N Points, daily freebies etc.) minimal and pointless
|
}
|
||||||
// 0C = N Boost course, ultra luxury course that ruins the game if in use
|
|
||||||
err := s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", pkt.CharID0).Scan(&rights)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rights := uint32(0x0E)
|
||||||
|
s.server.db.QueryRow("SELECT rights FROM users u INNER JOIN characters c ON u.id = c.user_id WHERE c.id = $1", pkt.CharID0).Scan(&rights)
|
||||||
|
|
||||||
s.Lock()
|
s.Lock()
|
||||||
s.charID = pkt.CharID0
|
s.charID = pkt.CharID0
|
||||||
s.rights = rights
|
s.rights = rights
|
||||||
@@ -157,7 +160,7 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp
|
bf.WriteUint32(uint32(Time_Current_Adjusted().Unix())) // Unix timestamp
|
||||||
|
|
||||||
_, err = s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID)
|
_, err := s.server.db.Exec("UPDATE servers SET current_players=$1 WHERE server_id=$2", len(s.server.sessions), s.server.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -342,16 +345,161 @@ func handleMsgSysRightsReload(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfTransitMessage)
|
pkt := p.(*mhfpacket.MsgMhfTransitMessage)
|
||||||
// TODO: figure out what this is supposed to return
|
resp := byteframe.NewByteFrame()
|
||||||
// probably what world+land the targeted character is on?
|
resp.WriteUint16(0)
|
||||||
// stubbed response will just say user not found
|
var count uint16
|
||||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
switch pkt.SearchType {
|
||||||
|
case 1: // CID
|
||||||
|
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
|
||||||
|
CharID := bf.ReadUint32()
|
||||||
|
for _, c := range s.server.Channels {
|
||||||
|
for _, session := range c.sessions {
|
||||||
|
if session.charID == CharID {
|
||||||
|
count++
|
||||||
|
sessionName := stringsupport.UTF8ToSJIS(session.Name)
|
||||||
|
sessionStage := stringsupport.UTF8ToSJIS(session.stageID)
|
||||||
|
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
|
||||||
|
resp.WriteUint16(c.Port)
|
||||||
|
resp.WriteUint32(session.charID)
|
||||||
|
resp.WriteBool(true)
|
||||||
|
resp.WriteUint8(uint8(len(sessionName) + 1))
|
||||||
|
resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{charID: session.charID, index: 3}])))
|
||||||
|
resp.WriteBytes(make([]byte, 40))
|
||||||
|
resp.WriteUint8(uint8(len(sessionStage) + 1))
|
||||||
|
resp.WriteBytes(make([]byte, 8))
|
||||||
|
resp.WriteNullTerminatedBytes(sessionName)
|
||||||
|
resp.WriteBytes(c.userBinaryParts[userBinaryPartID{session.charID, 3}])
|
||||||
|
resp.WriteNullTerminatedBytes(sessionStage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2: // Name
|
||||||
|
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
|
||||||
|
bf.ReadUint16() // lenSearchTerm
|
||||||
|
bf.ReadUint16() // maxResults
|
||||||
|
bf.ReadUint8() // Unk
|
||||||
|
searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
|
||||||
|
for _, c := range s.server.Channels {
|
||||||
|
for _, session := range c.sessions {
|
||||||
|
if count == 100 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if strings.Contains(session.Name, searchTerm) {
|
||||||
|
count++
|
||||||
|
sessionName := stringsupport.UTF8ToSJIS(session.Name)
|
||||||
|
sessionStage := stringsupport.UTF8ToSJIS(session.stageID)
|
||||||
|
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
|
||||||
|
resp.WriteUint16(c.Port)
|
||||||
|
resp.WriteUint32(session.charID)
|
||||||
|
resp.WriteBool(true)
|
||||||
|
resp.WriteUint8(uint8(len(sessionName) + 1))
|
||||||
|
resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{session.charID, 3}])))
|
||||||
|
resp.WriteBytes(make([]byte, 40))
|
||||||
|
resp.WriteUint8(uint8(len(sessionStage) + 1))
|
||||||
|
resp.WriteBytes(make([]byte, 8))
|
||||||
|
resp.WriteNullTerminatedBytes(sessionName)
|
||||||
|
resp.WriteBytes(c.userBinaryParts[userBinaryPartID{charID: session.charID, index: 3}])
|
||||||
|
resp.WriteNullTerminatedBytes(sessionStage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 3: // Enumerate Party
|
||||||
|
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
|
||||||
|
ip := bf.ReadBytes(4)
|
||||||
|
ipString := fmt.Sprintf("%d.%d.%d.%d", ip[3], ip[2], ip[1], ip[0])
|
||||||
|
port := bf.ReadUint16()
|
||||||
|
bf.ReadUint16() // lenStage
|
||||||
|
maxResults := bf.ReadUint16()
|
||||||
|
bf.ReadBytes(1)
|
||||||
|
stageID := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
|
||||||
|
for _, c := range s.server.Channels {
|
||||||
|
if c.IP == ipString && c.Port == port {
|
||||||
|
for _, stage := range c.stages {
|
||||||
|
if stage.id == stageID {
|
||||||
|
if count == maxResults {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for session := range stage.clients {
|
||||||
|
count++
|
||||||
|
sessionStage := stringsupport.UTF8ToSJIS(session.stageID)
|
||||||
|
sessionName := stringsupport.UTF8ToSJIS(session.Name)
|
||||||
|
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
|
||||||
|
resp.WriteUint16(c.Port)
|
||||||
|
resp.WriteUint32(session.charID)
|
||||||
|
resp.WriteUint8(uint8(len(sessionStage) + 1))
|
||||||
|
resp.WriteUint8(uint8(len(sessionName) + 1))
|
||||||
|
resp.WriteUint8(0)
|
||||||
|
resp.WriteUint8(7) // lenBinary
|
||||||
|
resp.WriteBytes(make([]byte, 48))
|
||||||
|
resp.WriteNullTerminatedBytes(sessionStage)
|
||||||
|
resp.WriteNullTerminatedBytes(sessionName)
|
||||||
|
resp.WriteUint16(999) // HR
|
||||||
|
resp.WriteUint16(999) // GR
|
||||||
|
resp.WriteBytes([]byte{0x06, 0x10, 0x00}) // Unk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 4: // Find Party
|
||||||
|
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
|
||||||
|
bf.ReadUint8()
|
||||||
|
maxResults := bf.ReadUint16()
|
||||||
|
bf.ReadUint8()
|
||||||
|
bf.ReadUint8()
|
||||||
|
partyType := bf.ReadUint16()
|
||||||
|
_ = bf.DataFromCurrent() // Restrictions
|
||||||
|
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) {
|
||||||
|
count++
|
||||||
|
sessionStage := stringsupport.UTF8ToSJIS(stage.id)
|
||||||
|
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
|
||||||
|
resp.WriteUint16(c.Port)
|
||||||
|
|
||||||
|
// TODO: This is half right, could be trimmed
|
||||||
|
resp.WriteUint16(0)
|
||||||
|
resp.WriteUint16(uint16(len(stage.clients)))
|
||||||
|
resp.WriteUint16(uint16(len(stage.clients)))
|
||||||
|
resp.WriteUint16(stage.maxPlayers)
|
||||||
|
resp.WriteUint16(0)
|
||||||
|
resp.WriteUint16(uint16(len(stage.clients)))
|
||||||
|
//
|
||||||
|
|
||||||
|
resp.WriteUint16(uint16(len(sessionStage) + 1))
|
||||||
|
resp.WriteUint8(1)
|
||||||
|
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}])))
|
||||||
|
resp.WriteBytes(make([]byte, 16))
|
||||||
|
resp.WriteNullTerminatedBytes(sessionStage)
|
||||||
|
resp.WriteBytes([]byte{0x00})
|
||||||
|
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp.Seek(0, io.SeekStart)
|
||||||
|
resp.WriteUint16(count)
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgCaExchangeItem(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgCaExchangeItem(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {}
|
|
||||||
|
|
||||||
func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfServerCommand(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfServerCommand(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
@@ -406,8 +554,6 @@ func handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfGetExtraInfo(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfGetExtraInfo(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) {}
|
|
||||||
|
|
||||||
func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateUnionItem)
|
pkt := p.(*mhfpacket.MsgMhfEnumerateUnionItem)
|
||||||
var boxContents []byte
|
var boxContents []byte
|
||||||
|
|||||||
@@ -3,86 +3,139 @@ package channelserver
|
|||||||
import (
|
import (
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/network/mhfpacket"
|
"erupe-ce/network/mhfpacket"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var achievementCurves = [][]int32{
|
||||||
|
// 0: HR weapon use, Class use, Tore dailies
|
||||||
|
{5, 15, 30, 50, 100, 150, 200, 300},
|
||||||
|
// 1: Weapon collector, G wep enhances
|
||||||
|
{1, 5, 10, 15, 30, 50, 75, 100},
|
||||||
|
// 2: Festa wins
|
||||||
|
{1, 2, 3, 4, 5, 6, 7, 8},
|
||||||
|
// 3: GR weapon use, Sigil crafts
|
||||||
|
{10, 50, 100, 200, 350, 500, 750, 999},
|
||||||
|
}
|
||||||
|
|
||||||
|
var achievementCurveMap = map[uint8][]int32{
|
||||||
|
0: achievementCurves[0], 1: achievementCurves[0], 2: achievementCurves[0], 3: achievementCurves[0],
|
||||||
|
4: achievementCurves[0], 5: achievementCurves[0], 6: achievementCurves[0], 7: achievementCurves[1],
|
||||||
|
8: achievementCurves[2], 9: achievementCurves[0], 10: achievementCurves[0], 11: achievementCurves[0],
|
||||||
|
12: achievementCurves[0], 13: achievementCurves[0], 14: achievementCurves[0], 15: achievementCurves[0],
|
||||||
|
16: achievementCurves[3], 17: achievementCurves[3], 18: achievementCurves[3], 19: achievementCurves[3],
|
||||||
|
20: achievementCurves[3], 21: achievementCurves[3], 22: achievementCurves[3], 23: achievementCurves[3],
|
||||||
|
24: achievementCurves[3], 25: achievementCurves[3], 26: achievementCurves[3], 27: achievementCurves[1],
|
||||||
|
28: achievementCurves[1], 29: achievementCurves[3], 30: achievementCurves[3], 31: achievementCurves[3],
|
||||||
|
32: achievementCurves[3],
|
||||||
|
}
|
||||||
|
|
||||||
|
type Achievement struct {
|
||||||
|
Level uint8
|
||||||
|
Value uint32
|
||||||
|
NextValue uint16
|
||||||
|
Required uint32
|
||||||
|
Updated bool
|
||||||
|
Progress uint32
|
||||||
|
Trophy uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAchData(id uint8, score int32) Achievement {
|
||||||
|
curve := achievementCurveMap[id]
|
||||||
|
var ach Achievement
|
||||||
|
for i, v := range curve {
|
||||||
|
temp := score - v
|
||||||
|
if temp < 0 {
|
||||||
|
ach.Progress = uint32(score)
|
||||||
|
ach.Required = uint32(curve[i])
|
||||||
|
switch ach.Level {
|
||||||
|
case 0:
|
||||||
|
ach.NextValue = 5
|
||||||
|
case 1, 2, 3:
|
||||||
|
ach.NextValue = 10
|
||||||
|
case 4, 5:
|
||||||
|
ach.NextValue = 15
|
||||||
|
case 6:
|
||||||
|
ach.NextValue = 15
|
||||||
|
ach.Trophy = 0x40
|
||||||
|
case 7:
|
||||||
|
ach.NextValue = 20
|
||||||
|
ach.Trophy = 0x60
|
||||||
|
}
|
||||||
|
return ach
|
||||||
|
} else {
|
||||||
|
score = temp
|
||||||
|
ach.Level++
|
||||||
|
switch ach.Level {
|
||||||
|
case 1:
|
||||||
|
ach.Value += 5
|
||||||
|
case 2, 3, 4:
|
||||||
|
ach.Value += 10
|
||||||
|
case 5, 6, 7:
|
||||||
|
ach.Value += 15
|
||||||
|
case 8:
|
||||||
|
ach.Value += 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ach.Required = uint32(curve[7])
|
||||||
|
ach.Trophy = 0x7F
|
||||||
|
ach.Progress = ach.Required
|
||||||
|
return ach
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfGetAchievement(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfGetAchievement(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetAchievement)
|
pkt := p.(*mhfpacket.MsgMhfGetAchievement)
|
||||||
|
|
||||||
achievementStruct := []struct {
|
var exists int
|
||||||
ID uint8 // Main ID
|
err := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", pkt.CharID).Scan(&exists)
|
||||||
Unk0 uint8 // always FF
|
if err != nil {
|
||||||
Unk1 uint16 // 0x05 0x00
|
s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", pkt.CharID)
|
||||||
Unk2 uint32 // 0x01 0x0A 0x05 0x00
|
|
||||||
}{
|
|
||||||
{ID: 0, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 1, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 2, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 3, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 4, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 5, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 6, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 7, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 8, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 9, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 10, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 11, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 12, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 13, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 14, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 15, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 16, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 17, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 18, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 19, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 20, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 21, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 22, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 23, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 24, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 25, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 26, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 27, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 28, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 29, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 30, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 31, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 32, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 33, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 34, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 35, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 36, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 37, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 38, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 39, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 40, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 41, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 42, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 43, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 44, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 45, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 46, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 47, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 48, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 49, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 50, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 51, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 52, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 53, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 54, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 55, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 56, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 57, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 58, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
{ID: 59, Unk0: 0xFF, Unk1: 0, Unk2: 0},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var scores [33]int32
|
||||||
|
err = s.server.db.QueryRow("SELECT * FROM achievements WHERE id=$1", pkt.CharID).Scan(&scores[0],
|
||||||
|
&scores[0], &scores[1], &scores[2], &scores[3], &scores[4], &scores[5], &scores[6], &scores[7], &scores[8],
|
||||||
|
&scores[9], &scores[10], &scores[11], &scores[12], &scores[13], &scores[14], &scores[15], &scores[16],
|
||||||
|
&scores[17], &scores[18], &scores[19], &scores[20], &scores[21], &scores[22], &scores[23], &scores[24],
|
||||||
|
&scores[25], &scores[26], &scores[27], &scores[28], &scores[29], &scores[30], &scores[31], &scores[32])
|
||||||
|
if err != nil {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 20))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
resp.WriteUint8(uint8(len(achievementStruct))) // Entry count
|
var points uint32
|
||||||
for _, entry := range achievementStruct {
|
resp.WriteBytes(make([]byte, 16))
|
||||||
resp.WriteUint8(entry.ID)
|
resp.WriteBytes([]byte{0x02, 0x00, 0x00}) // Unk
|
||||||
resp.WriteUint8(entry.Unk0)
|
|
||||||
resp.WriteUint16(entry.Unk1)
|
var id uint8
|
||||||
resp.WriteUint32(entry.Unk2)
|
entries := uint8(33)
|
||||||
|
resp.WriteUint8(entries) // Entry count
|
||||||
|
for id = 0; id < entries; id++ {
|
||||||
|
achData := GetAchData(id, scores[id])
|
||||||
|
points += achData.Value
|
||||||
|
resp.WriteUint8(id)
|
||||||
|
resp.WriteUint8(achData.Level)
|
||||||
|
resp.WriteUint16(achData.NextValue)
|
||||||
|
resp.WriteUint32(achData.Required)
|
||||||
|
resp.WriteBool(false) // TODO: Notify on rank increase since last checked, see MhfDisplayedAchievement
|
||||||
|
resp.WriteUint8(achData.Trophy)
|
||||||
|
/* Trophy bitfield
|
||||||
|
0000 0000
|
||||||
|
abcd efgh
|
||||||
|
B - Bronze (0x40)
|
||||||
|
B-C - Silver (0x60)
|
||||||
|
B-H - Gold (0x7F)
|
||||||
|
*/
|
||||||
|
resp.WriteUint16(0) // Unk
|
||||||
|
resp.WriteUint32(achData.Progress)
|
||||||
}
|
}
|
||||||
|
resp.Seek(0, io.SeekStart)
|
||||||
|
resp.WriteUint32(points)
|
||||||
|
resp.WriteUint32(points)
|
||||||
|
resp.WriteUint32(points)
|
||||||
|
resp.WriteUint32(points)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,11 +146,23 @@ func handleMsgMhfSetCaAchievementHist(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
func handleMsgMhfResetAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfResetAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfAddAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfAddAchievement(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfAddAchievement)
|
||||||
|
|
||||||
|
var exists int
|
||||||
|
err := s.server.db.QueryRow("SELECT id FROM achievements WHERE id=$1", s.charID).Scan(&exists)
|
||||||
|
if err != nil {
|
||||||
|
s.server.db.Exec("INSERT INTO achievements (id) VALUES ($1)", s.charID)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.server.db.Exec(fmt.Sprintf("UPDATE achievements SET ach%d=ach%d+1 WHERE id=$1", pkt.AchievementID, pkt.AchievementID), s.charID)
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfPaymentAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfPaymentAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfDisplayedAchievement(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfDisplayedAchievement(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
// This is how you would figure out if the rank-up notification needs to occur
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfGetCaAchievementHist(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfGetCaAchievementHist(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
BinaryMessageTypeState = 0
|
BinaryMessageTypeState = 0
|
||||||
BinaryMessageTypeChat = 1
|
BinaryMessageTypeChat = 1
|
||||||
|
BinaryMessageTypeData = 3
|
||||||
BinaryMessageTypeMailNotify = 4
|
BinaryMessageTypeMailNotify = 4
|
||||||
BinaryMessageTypeEmote = 6
|
BinaryMessageTypeEmote = 6
|
||||||
)
|
)
|
||||||
@@ -118,7 +119,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
// Send to the proper recipients.
|
// Send to the proper recipients.
|
||||||
switch pkt.BroadcastType {
|
switch pkt.BroadcastType {
|
||||||
case BroadcastTypeWorld:
|
case BroadcastTypeWorld:
|
||||||
s.server.WorldcastMHF(resp, s)
|
s.server.WorldcastMHF(resp, s, nil)
|
||||||
case BroadcastTypeStage:
|
case BroadcastTypeStage:
|
||||||
if isDiceCommand {
|
if isDiceCommand {
|
||||||
s.stage.BroadcastMHF(resp, nil) // send dice result back to caller
|
s.stage.BroadcastMHF(resp, nil) // send dice result back to caller
|
||||||
@@ -129,8 +130,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
if pkt.MessageType == 1 {
|
if pkt.MessageType == 1 {
|
||||||
raviSema := getRaviSemaphore(s)
|
raviSema := getRaviSemaphore(s)
|
||||||
if raviSema != "" {
|
if raviSema != "" {
|
||||||
sema := s.server.semaphore[raviSema]
|
s.server.BroadcastMHF(resp, s)
|
||||||
(*sema).BroadcastMHF(resp, s)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s.server.BroadcastMHF(resp, s)
|
s.server.BroadcastMHF(resp, s)
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
stage.RLock()
|
stage.RLock()
|
||||||
var clients []uint32
|
var clients []uint32
|
||||||
switch pkt.Unk1 {
|
switch pkt.Get {
|
||||||
|
case 0: // All
|
||||||
|
for _, cid := range stage.clients {
|
||||||
|
clients = append(clients, cid)
|
||||||
|
}
|
||||||
case 1: // Not ready
|
case 1: // Not ready
|
||||||
for cid, ready := range stage.reservedClientSlots {
|
for cid, ready := range stage.reservedClientSlots {
|
||||||
if !ready {
|
if !ready {
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
s.myseries.toreData = decompressedData[130228:130468] // 0x1FCB4 + 240
|
s.myseries.toreData = decompressedData[130228:130468] // 0x1FCB4 + 240
|
||||||
s.myseries.gardenData = decompressedData[142424:142492] // 0x22C58 + 68
|
s.myseries.gardenData = decompressedData[142424:142492] // 0x22C58 + 68
|
||||||
|
|
||||||
isMale := uint8(decompressedData[80]) // 0x50
|
isFemale := decompressedData[81] // 0x51
|
||||||
if isMale == 1 {
|
if isFemale == 1 {
|
||||||
_, err = s.server.db.Exec("UPDATE characters SET is_female=true WHERE id=$1", s.charID)
|
_, err = s.server.db.Exec("UPDATE characters SET is_female=true WHERE id=$1", s.charID)
|
||||||
} else {
|
} else {
|
||||||
_, err = s.server.db.Exec("UPDATE characters SET is_female=false WHERE id=$1", s.charID)
|
_, err = s.server.db.Exec("UPDATE characters SET is_female=false WHERE id=$1", s.charID)
|
||||||
@@ -290,21 +290,14 @@ func dumpSaveData(s *Session, data []byte, suffix string) {
|
|||||||
|
|
||||||
func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfLoaddata)
|
pkt := p.(*mhfpacket.MsgMhfLoaddata)
|
||||||
overrideFile := filepath.Join(".", "bin", "save_override.bin")
|
if _, err := os.Stat(filepath.Join(s.server.erupeConfig.BinPath, "save_override.bin")); err == nil {
|
||||||
var data []byte
|
data, _ := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, "save_override.bin"))
|
||||||
|
|
||||||
if _, err := os.Stat(overrideFile); err == nil {
|
|
||||||
file, err := os.Open(overrideFile)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
data, err := ioutil.ReadAll(file)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, data)
|
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var data []byte
|
||||||
|
|
||||||
err := s.server.db.QueryRow("SELECT savedata FROM characters WHERE id = $1", s.charID).Scan(&data)
|
err := s.server.db.QueryRow("SELECT savedata FROM characters WHERE id = $1", s.charID).Scan(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to get savedata from db", zap.Error(err))
|
s.logger.Fatal("Failed to get savedata from db", zap.Error(err))
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"erupe-ce/common/bfutil"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
ps "erupe-ce/common/pascalstring"
|
ps "erupe-ce/common/pascalstring"
|
||||||
"erupe-ce/common/stringsupport"
|
"erupe-ce/common/stringsupport"
|
||||||
@@ -55,6 +54,7 @@ type Guild struct {
|
|||||||
PugiName1 string `db:"pugi_name_1"`
|
PugiName1 string `db:"pugi_name_1"`
|
||||||
PugiName2 string `db:"pugi_name_2"`
|
PugiName2 string `db:"pugi_name_2"`
|
||||||
PugiName3 string `db:"pugi_name_3"`
|
PugiName3 string `db:"pugi_name_3"`
|
||||||
|
Recruiting bool `db:"recruiting"`
|
||||||
FestivalColour FestivalColour `db:"festival_colour"`
|
FestivalColour FestivalColour `db:"festival_colour"`
|
||||||
Souls uint32 `db:"souls"`
|
Souls uint32 `db:"souls"`
|
||||||
Rank uint16 `db:"rank"`
|
Rank uint16 `db:"rank"`
|
||||||
@@ -125,6 +125,7 @@ SELECT
|
|||||||
pugi_name_1,
|
pugi_name_1,
|
||||||
pugi_name_2,
|
pugi_name_2,
|
||||||
pugi_name_3,
|
pugi_name_3,
|
||||||
|
recruiting,
|
||||||
CASE WHEN (
|
CASE WHEN (
|
||||||
SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id
|
SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id
|
||||||
) IS NULL THEN 'none' ELSE (
|
) IS NULL THEN 'none' ELSE (
|
||||||
@@ -518,36 +519,6 @@ func rollbackTransaction(s *Session, transaction *sql.Tx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindGuildsByName(s *Session, name string) ([]*Guild, error) {
|
|
||||||
searchTerm := fmt.Sprintf("%%%s%%", name)
|
|
||||||
|
|
||||||
rows, err := s.server.db.Queryx(fmt.Sprintf(`
|
|
||||||
%s
|
|
||||||
WHERE g.name ILIKE $1
|
|
||||||
`, guildInfoSelectQuery), searchTerm)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("failed to find guilds for search term", zap.Error(err), zap.String("searchTerm", name))
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
guilds := make([]*Guild, 0)
|
|
||||||
|
|
||||||
for rows.Next() {
|
|
||||||
guild, err := buildGuildObjectFromDbResult(rows, err, s)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
guilds = append(guilds, guild)
|
|
||||||
}
|
|
||||||
|
|
||||||
return guilds, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetGuildInfoByID(s *Session, guildID uint32) (*Guild, error) {
|
func GetGuildInfoByID(s *Session, guildID uint32) (*Guild, error) {
|
||||||
rows, err := s.server.db.Queryx(fmt.Sprintf(`
|
rows, err := s.server.db.Queryx(fmt.Sprintf(`
|
||||||
%s
|
%s
|
||||||
@@ -676,6 +647,8 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteUint32(uint32(response))
|
bf.WriteUint32(uint32(response))
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
return
|
||||||
case mhfpacket.OPERATE_GUILD_APPLY:
|
case mhfpacket.OPERATE_GUILD_APPLY:
|
||||||
err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil)
|
err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil)
|
||||||
|
|
||||||
@@ -685,6 +658,8 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
} else {
|
} else {
|
||||||
bf.WriteUint32(guild.LeaderCharID)
|
bf.WriteUint32(guild.LeaderCharID)
|
||||||
}
|
}
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
return
|
||||||
case mhfpacket.OPERATE_GUILD_LEAVE:
|
case mhfpacket.OPERATE_GUILD_LEAVE:
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
@@ -701,81 +676,53 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteUint32(uint32(response))
|
bf.WriteUint32(uint32(response))
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
return
|
||||||
case mhfpacket.OPERATE_GUILD_DONATE_RANK:
|
case mhfpacket.OPERATE_GUILD_DONATE_RANK:
|
||||||
handleDonateRP(s, pkt, bf, guild, false)
|
handleDonateRP(s, pkt, bf, guild, false)
|
||||||
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_DENY:
|
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_DENY:
|
||||||
// TODO: close applications for guild
|
s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID)
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_ALLOW:
|
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_ALLOW:
|
||||||
// TODO: open applications for guild
|
s.server.db.Exec("UPDATE guilds SET recruiting=true WHERE id=$1", guild.ID)
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE:
|
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE:
|
||||||
handleAvoidLeadershipUpdate(s, pkt, true)
|
handleAvoidLeadershipUpdate(s, pkt, true)
|
||||||
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE:
|
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE:
|
||||||
handleAvoidLeadershipUpdate(s, pkt, false)
|
handleAvoidLeadershipUpdate(s, pkt, false)
|
||||||
case mhfpacket.OPERATE_GUILD_UPDATE_COMMENT:
|
case mhfpacket.OPERATE_GUILD_UPDATE_COMMENT:
|
||||||
pbf := byteframe.NewByteFrameFromBytes(pkt.UnkData)
|
pbf := byteframe.NewByteFrameFromBytes(pkt.UnkData)
|
||||||
|
|
||||||
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
|
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
|
||||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = pbf.ReadUint8() // len
|
_ = pbf.ReadUint8() // len
|
||||||
_ = pbf.ReadUint32()
|
_ = pbf.ReadUint32()
|
||||||
guild.Comment = stringsupport.SJISToUTF8(pbf.ReadNullTerminatedBytes())
|
guild.Comment = stringsupport.SJISToUTF8(pbf.ReadNullTerminatedBytes())
|
||||||
err = guild.Save(s)
|
guild.Save(s)
|
||||||
if err != nil {
|
|
||||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bf.WriteUint32(0x00)
|
|
||||||
case mhfpacket.OPERATE_GUILD_UPDATE_MOTTO:
|
case mhfpacket.OPERATE_GUILD_UPDATE_MOTTO:
|
||||||
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
|
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
|
||||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guild.SubMotto = pkt.UnkData[3]
|
guild.SubMotto = pkt.UnkData[3]
|
||||||
guild.MainMotto = pkt.UnkData[4]
|
guild.MainMotto = pkt.UnkData[4]
|
||||||
|
guild.Save(s)
|
||||||
err := guild.Save(s)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_1:
|
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_1:
|
||||||
handleRenamePugi(s, pkt.UnkData, guild, 1)
|
handleRenamePugi(s, pkt.UnkData, guild, 1)
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_2:
|
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_2:
|
||||||
handleRenamePugi(s, pkt.UnkData, guild, 2)
|
handleRenamePugi(s, pkt.UnkData, guild, 2)
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_3:
|
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_3:
|
||||||
handleRenamePugi(s, pkt.UnkData, guild, 3)
|
handleRenamePugi(s, pkt.UnkData, guild, 3)
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_1:
|
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_1:
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
// TODO: decode guild poogie outfits
|
||||||
return
|
|
||||||
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_2:
|
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_2:
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_3:
|
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_3:
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
case mhfpacket.OPERATE_GUILD_DONATE_EVENT:
|
case mhfpacket.OPERATE_GUILD_DONATE_EVENT:
|
||||||
handleDonateRP(s, pkt, bf, guild, true)
|
handleDonateRP(s, pkt, bf, guild, true)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unhandled operate guild action '%d'", pkt.Action))
|
panic(fmt.Sprintf("unhandled operate guild action '%d'", pkt.Action))
|
||||||
}
|
}
|
||||||
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleRenamePugi(s *Session, data []byte, guild *Guild, num int) {
|
func handleRenamePugi(s *Session, data []byte, guild *Guild, num int) {
|
||||||
@@ -860,42 +807,45 @@ func handleMsgMhfOperateGuildMember(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if pkt.Action == mhfpacket.OPERATE_GUILD_MEMBER_ACTION_ACCEPT || pkt.Action == mhfpacket.OPERATE_GUILD_MEMBER_ACTION_REJECT {
|
var mail Mail
|
||||||
switch pkt.Action {
|
|
||||||
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_ACCEPT:
|
|
||||||
err = guild.AcceptApplication(s, pkt.CharID)
|
|
||||||
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_REJECT:
|
|
||||||
err = guild.RejectApplication(s, pkt.CharID)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
character, err := GetCharacterGuildData(s, pkt.CharID)
|
|
||||||
|
|
||||||
if err != nil || character == nil {
|
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch pkt.Action {
|
switch pkt.Action {
|
||||||
|
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_ACCEPT:
|
||||||
|
err = guild.AcceptApplication(s, pkt.CharID)
|
||||||
|
mail = Mail{
|
||||||
|
SenderID: s.charID,
|
||||||
|
RecipientID: pkt.CharID,
|
||||||
|
Subject: "Accepted!",
|
||||||
|
Body: fmt.Sprintf("Your application to join 「%s」 was accepted.", guild.Name),
|
||||||
|
IsGuildInvite: false,
|
||||||
|
}
|
||||||
|
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_REJECT:
|
||||||
|
err = guild.RejectApplication(s, pkt.CharID)
|
||||||
|
mail = Mail{
|
||||||
|
SenderID: s.charID,
|
||||||
|
RecipientID: pkt.CharID,
|
||||||
|
Subject: "Rejected",
|
||||||
|
Body: fmt.Sprintf("Your application to join 「%s」 was rejected.", guild.Name),
|
||||||
|
IsGuildInvite: false,
|
||||||
|
}
|
||||||
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_KICK:
|
case mhfpacket.OPERATE_GUILD_MEMBER_ACTION_KICK:
|
||||||
err = guild.RemoveCharacter(s, pkt.CharID)
|
err = guild.RemoveCharacter(s, pkt.CharID)
|
||||||
|
mail = Mail{
|
||||||
|
SenderID: s.charID,
|
||||||
|
RecipientID: pkt.CharID,
|
||||||
|
Subject: "Kicked",
|
||||||
|
Body: fmt.Sprintf("You were kicked from 「%s」.", guild.Name),
|
||||||
|
IsGuildInvite: false,
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
doAckSimpleFail(s, pkt.AckHandle, nil)
|
||||||
panic(fmt.Sprintf("unhandled operateGuildMember action '%d'", pkt.Action))
|
s.logger.Warn(fmt.Sprintf("unhandled operateGuildMember action '%d'", pkt.Action))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
doAckSimpleFail(s, pkt.AckHandle, nil)
|
||||||
return
|
} else {
|
||||||
|
mail.Send(s, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, nil)
|
doAckSimpleSucceed(s, pkt.AckHandle, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -912,6 +862,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && guild != nil {
|
if err == nil && guild != nil {
|
||||||
|
s.prevGuildID = guild.ID
|
||||||
|
|
||||||
guildName := stringsupport.UTF8ToSJIS(guild.Name)
|
guildName := stringsupport.UTF8ToSJIS(guild.Name)
|
||||||
guildComment := stringsupport.UTF8ToSJIS(guild.Comment)
|
guildComment := stringsupport.UTF8ToSJIS(guild.Comment)
|
||||||
guildLeaderName := stringsupport.UTF8ToSJIS(guild.LeaderName)
|
guildLeaderName := stringsupport.UTF8ToSJIS(guild.LeaderName)
|
||||||
@@ -943,7 +895,9 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf.WriteUint8(guild.SubMotto)
|
bf.WriteUint8(guild.SubMotto)
|
||||||
|
|
||||||
// Unk appears to be static
|
// Unk appears to be static
|
||||||
bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
|
||||||
|
|
||||||
|
bf.WriteBool(!guild.Recruiting)
|
||||||
|
|
||||||
if characterGuildData == nil || characterGuildData.IsApplicant {
|
if characterGuildData == nil || characterGuildData.IsApplicant {
|
||||||
bf.WriteUint16(0x00)
|
bf.WriteUint16(0x00)
|
||||||
@@ -1063,19 +1017,22 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
resp.WriteUint32(0) // Count
|
resp.WriteUint32(0) // Count
|
||||||
resp.WriteUint8(5) // Unk, read if count == 0.
|
resp.WriteUint8(0) // Unk, read if count == 0.
|
||||||
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
bf.WriteUint16(uint16(len(applicants)))
|
bf.WriteUint16(0)
|
||||||
|
} else {
|
||||||
for _, applicant := range applicants {
|
bf.WriteUint16(uint16(len(applicants)))
|
||||||
bf.WriteUint32(applicant.CharID)
|
for _, applicant := range applicants {
|
||||||
bf.WriteUint32(0x05)
|
bf.WriteUint32(applicant.CharID)
|
||||||
bf.WriteUint16(0x0032)
|
bf.WriteUint16(0)
|
||||||
bf.WriteUint8(0x00)
|
bf.WriteUint16(0)
|
||||||
ps.Uint16(bf, applicant.Name, true)
|
bf.WriteUint16(applicant.HRP)
|
||||||
|
bf.WriteUint16(applicant.GR)
|
||||||
|
ps.Uint8(bf, applicant.Name, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteUint16(0x0000)
|
bf.WriteUint16(0x0000)
|
||||||
@@ -1134,107 +1091,94 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
switch pkt.Type {
|
switch pkt.Type {
|
||||||
case mhfpacket.ENUMERATE_GUILD_TYPE_GUILD_NAME:
|
case mhfpacket.ENUMERATE_GUILD_TYPE_GUILD_NAME:
|
||||||
bf.ReadBytes(8)
|
bf.ReadBytes(10)
|
||||||
searchTermLength := bf.ReadUint16()
|
searchTerm := fmt.Sprintf(`%%%s%%`, stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()))
|
||||||
bf.ReadBytes(1)
|
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE g.name ILIKE $1 OFFSET $2`, guildInfoSelectQuery), searchTerm, pkt.Page*10)
|
||||||
searchTerm := bf.ReadBytes(uint(searchTermLength))
|
if err == nil {
|
||||||
var searchTermSafe string
|
for rows.Next() {
|
||||||
searchTermSafe, err = s.clientContext.StrConv.Decode(bfutil.UpToNull(searchTerm))
|
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||||
if err != nil {
|
guilds = append(guilds, guild)
|
||||||
panic(err)
|
}
|
||||||
}
|
}
|
||||||
guilds, err = FindGuildsByName(s, searchTermSafe)
|
|
||||||
case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_NAME:
|
case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_NAME:
|
||||||
bf.ReadBytes(8)
|
bf.ReadBytes(10)
|
||||||
searchTermLength := bf.ReadUint16()
|
searchTerm := fmt.Sprintf(`%%%s%%`, stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()))
|
||||||
bf.ReadBytes(1)
|
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE lc.name ILIKE $1 OFFSET $2`, guildInfoSelectQuery), searchTerm, pkt.Page*10)
|
||||||
searchTerm := bf.ReadBytes(uint(searchTermLength))
|
if err == nil {
|
||||||
var searchTermSafe string
|
|
||||||
searchTermSafe, err = s.clientContext.StrConv.Decode(bfutil.UpToNull(searchTerm))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE lc.name ILIKE $1`, guildInfoSelectQuery), searchTermSafe)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to retrieve guild by leader name", zap.Error(err))
|
|
||||||
} else {
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||||
guilds = append(guilds, guild)
|
guilds = append(guilds, guild)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_ID:
|
case mhfpacket.ENUMERATE_GUILD_TYPE_LEADER_ID:
|
||||||
bf.ReadBytes(3)
|
bf.ReadBytes(2)
|
||||||
ID := bf.ReadUint32()
|
ID := bf.ReadUint32()
|
||||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE leader_id = $1`, guildInfoSelectQuery), ID)
|
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE leader_id = $1`, guildInfoSelectQuery), ID)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
s.logger.Error("Failed to retrieve guild by leader ID", zap.Error(err))
|
|
||||||
} else {
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||||
guilds = append(guilds, guild)
|
guilds = append(guilds, guild)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_MEMBERS:
|
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_MEMBERS:
|
||||||
sorting := bf.ReadUint16()
|
sorting := bf.ReadUint8()
|
||||||
if sorting == 1 {
|
if sorting == 1 {
|
||||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count DESC`, guildInfoSelectQuery))
|
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||||
} else {
|
} else {
|
||||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count ASC`, guildInfoSelectQuery))
|
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY member_count ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err == nil {
|
||||||
s.logger.Error("Failed to retrieve guild by member count", zap.Error(err))
|
|
||||||
} else {
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||||
guilds = append(guilds, guild)
|
guilds = append(guilds, guild)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_REGISTRATION:
|
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_REGISTRATION:
|
||||||
sorting := bf.ReadUint16()
|
sorting := bf.ReadUint8()
|
||||||
if sorting == 1 {
|
if sorting == 1 {
|
||||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id ASC`, guildInfoSelectQuery))
|
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||||
} else {
|
} else {
|
||||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id DESC`, guildInfoSelectQuery))
|
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY id DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err == nil {
|
||||||
s.logger.Error("Failed to retrieve guild by registration date", zap.Error(err))
|
|
||||||
} else {
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||||
guilds = append(guilds, guild)
|
guilds = append(guilds, guild)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_RANK:
|
case mhfpacket.ENUMERATE_GUILD_TYPE_ORDER_RANK:
|
||||||
sorting := bf.ReadUint16()
|
sorting := bf.ReadUint8()
|
||||||
if sorting == 1 {
|
if sorting == 1 {
|
||||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp DESC`, guildInfoSelectQuery))
|
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||||
} else {
|
} else {
|
||||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp ASC`, guildInfoSelectQuery))
|
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s ORDER BY rank_rp ASC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err == nil {
|
||||||
s.logger.Error("Failed to retrieve guild by rank", zap.Error(err))
|
|
||||||
} else {
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||||
guilds = append(guilds, guild)
|
guilds = append(guilds, guild)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case mhfpacket.ENUMERATE_GUILD_TYPE_MOTTO:
|
case mhfpacket.ENUMERATE_GUILD_TYPE_MOTTO:
|
||||||
bf.ReadBytes(3)
|
bf.ReadBytes(2)
|
||||||
mainMotto := bf.ReadUint16()
|
mainMotto := bf.ReadUint16()
|
||||||
subMotto := bf.ReadUint16()
|
subMotto := bf.ReadUint16()
|
||||||
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE main_motto = $1 AND sub_motto = $2`, guildInfoSelectQuery), mainMotto, subMotto)
|
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE main_motto = $1 AND sub_motto = $2 OFFSET $3`, guildInfoSelectQuery), mainMotto, subMotto, pkt.Page*10)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
s.logger.Error("Failed to retrieve guild by motto", zap.Error(err))
|
|
||||||
} else {
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||||
guilds = append(guilds, guild)
|
guilds = append(guilds, guild)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case mhfpacket.ENUMERATE_GUILD_TYPE_RECRUITING:
|
case mhfpacket.ENUMERATE_GUILD_TYPE_RECRUITING:
|
||||||
//
|
// Assume the player wants the newest guilds with open recruitment
|
||||||
|
rows, err = s.server.db.Queryx(fmt.Sprintf(`%s WHERE recruiting=true ORDER BY id DESC OFFSET $1`, guildInfoSelectQuery), pkt.Page*10)
|
||||||
|
if err == nil {
|
||||||
|
for rows.Next() {
|
||||||
|
guild, _ := buildGuildObjectFromDbResult(rows, err, s)
|
||||||
|
guilds = append(guilds, guild)
|
||||||
|
}
|
||||||
|
}
|
||||||
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME:
|
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_ALLIANCE_NAME:
|
||||||
//
|
//
|
||||||
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME:
|
case mhfpacket.ENUMERATE_ALLIANCE_TYPE_LEADER_NAME:
|
||||||
@@ -1257,23 +1201,21 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf = byteframe.NewByteFrame()
|
bf = byteframe.NewByteFrame()
|
||||||
bf.WriteUint16(uint16(len(guilds)))
|
bf.WriteUint16(uint16(len(guilds)))
|
||||||
|
|
||||||
|
bf.WriteUint8(0x01) // Unk
|
||||||
|
|
||||||
for _, guild := range guilds {
|
for _, guild := range guilds {
|
||||||
bf.WriteUint8(0x00) // Unk
|
|
||||||
bf.WriteUint32(guild.ID)
|
bf.WriteUint32(guild.ID)
|
||||||
bf.WriteUint32(guild.LeaderCharID)
|
bf.WriteUint32(guild.LeaderCharID)
|
||||||
bf.WriteUint16(guild.MemberCount)
|
bf.WriteUint16(guild.MemberCount)
|
||||||
bf.WriteUint8(0x00) // Unk
|
bf.WriteUint16(0x0000) // Unk
|
||||||
bf.WriteUint8(0x00) // Unk
|
bf.WriteUint16(guild.Rank) // OR guilds in alliance
|
||||||
bf.WriteUint16(guild.Rank)
|
|
||||||
bf.WriteUint32(uint32(guild.CreatedAt.Unix()))
|
bf.WriteUint32(uint32(guild.CreatedAt.Unix()))
|
||||||
ps.Uint8(bf, guild.Name, true)
|
ps.Uint8(bf, guild.Name, true)
|
||||||
ps.Uint8(bf, guild.LeaderName, true)
|
ps.Uint8(bf, guild.LeaderName, true)
|
||||||
bf.WriteUint8(0x01) // Unk
|
bf.WriteUint8(0x01) // Unk
|
||||||
|
bf.WriteBool(!guild.Recruiting)
|
||||||
}
|
}
|
||||||
|
|
||||||
bf.WriteUint8(0x01) // Unk
|
|
||||||
bf.WriteUint8(0x00) // Unk
|
|
||||||
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1324,6 +1266,10 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
guild, err = GetGuildInfoByCharacterId(s, s.charID)
|
guild, err = GetGuildInfoByCharacterId(s, s.charID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if guild == nil && s.prevGuildID > 0 {
|
||||||
|
guild, err = GetGuildInfoByID(s, s.prevGuildID)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Warn("failed to retrieve guild sending no result message")
|
s.logger.Warn("failed to retrieve guild sending no result message")
|
||||||
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2))
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2))
|
||||||
@@ -1417,6 +1363,15 @@ func handleMsgMhfGetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
guild, err := GetGuildInfoByCharacterId(s, s.charID)
|
guild, err := GetGuildInfoByCharacterId(s, s.charID)
|
||||||
|
|
||||||
|
if guild == nil && s.prevGuildID != 0 {
|
||||||
|
guild, err = GetGuildInfoByID(s, s.prevGuildID)
|
||||||
|
s.prevGuildID = 0
|
||||||
|
if guild == nil || err != nil {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Warn("failed to respond to manage rights message")
|
s.logger.Warn("failed to respond to manage rights message")
|
||||||
return
|
return
|
||||||
@@ -1438,7 +1393,8 @@ func handleMsgMhfGetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
for _, member := range members {
|
for _, member := range members {
|
||||||
bf.WriteUint32(member.CharID)
|
bf.WriteUint32(member.CharID)
|
||||||
bf.WriteUint32(0x0)
|
bf.WriteBool(member.Recruiter)
|
||||||
|
bf.WriteBytes(make([]byte, 3))
|
||||||
}
|
}
|
||||||
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
@@ -1930,9 +1886,16 @@ func handleMsgMhfGenerateUdGuildMap(s *Session, p mhfpacket.MHFPacket) {}
|
|||||||
|
|
||||||
func handleMsgMhfUpdateGuild(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfUpdateGuild(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfSetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfSetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfSetGuildManageRight)
|
||||||
|
s.server.db.Exec("UPDATE guild_characters SET recruiter=$1 WHERE character_id=$2", pkt.Allowed, pkt.CharID)
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfEnumerateInvGuild)
|
||||||
|
stubEnumerateNoResults(s, pkt.AckHandle)
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfOperationInvGuild(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ type GuildMember struct {
|
|||||||
IsApplicant bool `db:"is_applicant"`
|
IsApplicant bool `db:"is_applicant"`
|
||||||
OrderIndex uint8 `db:"order_index"`
|
OrderIndex uint8 `db:"order_index"`
|
||||||
LastLogin uint32 `db:"last_login"`
|
LastLogin uint32 `db:"last_login"`
|
||||||
|
Recruiter bool `db:"recruiter"`
|
||||||
AvoidLeadership bool `db:"avoid_leadership"`
|
AvoidLeadership bool `db:"avoid_leadership"`
|
||||||
IsLeader bool `db:"is_leader"`
|
IsLeader bool `db:"is_leader"`
|
||||||
HRP uint16 `db:"hrp"`
|
HRP uint16 `db:"hrp"`
|
||||||
@@ -44,11 +45,6 @@ func (gm *GuildMember) Save(s *Session) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO add the recruiter permission to this check when it exists
|
|
||||||
func (gm *GuildMember) IsRecruiter() bool {
|
|
||||||
return gm.IsLeader || gm.IsSubLeader()
|
|
||||||
}
|
|
||||||
|
|
||||||
const guildMembersSelectSQL = `
|
const guildMembersSelectSQL = `
|
||||||
SELECT
|
SELECT
|
||||||
g.id as guild_id,
|
g.id as guild_id,
|
||||||
@@ -58,6 +54,7 @@ SELECT
|
|||||||
character.character_id,
|
character.character_id,
|
||||||
coalesce(gc.order_index, 0) as order_index,
|
coalesce(gc.order_index, 0) as order_index,
|
||||||
c.last_login,
|
c.last_login,
|
||||||
|
coalesce(gc.recruiter, false) as recruiter,
|
||||||
coalesce(gc.avoid_leadership, false) as avoid_leadership,
|
coalesce(gc.avoid_leadership, false) as avoid_leadership,
|
||||||
c.hrp,
|
c.hrp,
|
||||||
c.gr,
|
c.gr,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ func handleMsgMhfPostGuildScout(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if actorCharGuildData == nil || !actorCharGuildData.IsRecruiter() {
|
if actorCharGuildData == nil || !actorCharGuildData.Recruiter {
|
||||||
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ func handleMsgMhfCancelGuildScout(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if guildCharData == nil || !guildCharData.IsRecruiter() {
|
if guildCharData == nil || !guildCharData.Recruiter {
|
||||||
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -190,13 +190,15 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
guildInfo, err := GetGuildInfoByCharacterId(s, s.charID)
|
guildInfo, err := GetGuildInfoByCharacterId(s, s.charID)
|
||||||
|
|
||||||
if err != nil {
|
if guildInfo == nil && s.prevGuildID == 0 {
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if guildInfo == nil {
|
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
doAckSimpleFail(s, pkt.AckHandle, nil)
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
guildInfo, err = GetGuildInfoByID(s, s.prevGuildID)
|
||||||
|
if guildInfo == nil || err != nil {
|
||||||
|
doAckSimpleFail(s, pkt.AckHandle, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := s.server.db.Queryx(`
|
rows, err := s.server.db.Queryx(`
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
hunts := 0
|
hunts := 0
|
||||||
rows, _ := s.server.db.Queryx("SELECT id, host_id, destination, level, return, acquired, claimed, hunters, treasure, hunt_data FROM guild_hunts WHERE guild_id=$1", guild.ID)
|
rows, _ := s.server.db.Queryx("SELECT id, host_id, destination, level, return, acquired, claimed, hunters, treasure, hunt_data FROM guild_hunts WHERE guild_id=$1 AND $2 < return+604800", guild.ID, Time_Current_Adjusted().Unix())
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
hunt := &TreasureHunt{}
|
hunt := &TreasureHunt{}
|
||||||
err = rows.StructScan(&hunt)
|
err = rows.StructScan(&hunt)
|
||||||
@@ -51,6 +51,9 @@ func handleMsgMhfEnumerateGuildTresure(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf.WriteBytes(hunt.HuntData)
|
bf.WriteBytes(hunt.HuntData)
|
||||||
break
|
break
|
||||||
} else if pkt.MaxHunts == 30 && hunt.Acquired && hunt.Level == 2 {
|
} else if pkt.MaxHunts == 30 && hunt.Acquired && hunt.Level == 2 {
|
||||||
|
if hunts == 30 {
|
||||||
|
break
|
||||||
|
}
|
||||||
hunts++
|
hunts++
|
||||||
bf.WriteUint32(hunt.HuntID)
|
bf.WriteUint32(hunt.HuntID)
|
||||||
bf.WriteUint32(hunt.Destination)
|
bf.WriteUint32(hunt.Destination)
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"erupe-ce/common/stringsupport"
|
"erupe-ce/common/stringsupport"
|
||||||
"erupe-ce/network/mhfpacket"
|
"erupe-ce/network/mhfpacket"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) {
|
||||||
@@ -291,25 +293,54 @@ func handleMsgMhfSaveDecoMyset(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Title struct {
|
||||||
|
ID uint16 `db:"id"`
|
||||||
|
Acquired time.Time `db:"unlocked_at"`
|
||||||
|
Updated time.Time `db:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
func handleMsgMhfEnumerateTitle(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfEnumerateTitle(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateTitle)
|
pkt := p.(*mhfpacket.MsgMhfEnumerateTitle)
|
||||||
|
var count uint16
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
if pkt.CharID == 0 {
|
bf.WriteUint16(0)
|
||||||
titleCount := 114 // all titles unlocked
|
bf.WriteUint16(0) // Unk
|
||||||
bf.WriteUint16(uint16(titleCount)) // title count
|
rows, err := s.server.db.Queryx("SELECT id, unlocked_at, updated_at FROM titles WHERE char_id=$1", s.charID)
|
||||||
bf.WriteUint16(0) // unk
|
if err != nil {
|
||||||
for i := 0; i < titleCount; i++ {
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
bf.WriteUint16(uint16(i))
|
return
|
||||||
bf.WriteUint16(0) // unk
|
|
||||||
bf.WriteUint32(0) // timestamp acquired
|
|
||||||
bf.WriteUint32(0) // timestamp updated
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bf.WriteUint16(0)
|
|
||||||
}
|
}
|
||||||
|
for rows.Next() {
|
||||||
|
title := &Title{}
|
||||||
|
err = rows.StructScan(&title)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
bf.WriteUint16(title.ID)
|
||||||
|
bf.WriteUint16(0) // Unk
|
||||||
|
bf.WriteUint32(uint32(title.Acquired.Unix()))
|
||||||
|
bf.WriteUint32(uint32(title.Updated.Unix()))
|
||||||
|
}
|
||||||
|
bf.Seek(0, io.SeekStart)
|
||||||
|
bf.WriteUint16(count)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
pkt := p.(*mhfpacket.MsgMhfAcquireTitle)
|
||||||
|
var exists int
|
||||||
|
err := s.server.db.QueryRow("SELECT count(*) FROM titles WHERE id=$1 AND char_id=$2", pkt.TitleID, s.charID).Scan(&exists)
|
||||||
|
if err != nil || exists == 0 {
|
||||||
|
s.server.db.Exec("INSERT INTO titles VALUES ($1, $2, now(), now())", pkt.TitleID, s.charID)
|
||||||
|
} else {
|
||||||
|
s.server.db.Exec("UPDATE titles SET updated_at=now()")
|
||||||
|
}
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|
||||||
func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package channelserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"erupe-ce/common/stringsupport"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
@@ -275,7 +276,7 @@ func handleMsgMhfReadMail(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
|
|
||||||
body := s.clientContext.StrConv.MustEncode(mail.Body)
|
body := stringsupport.UTF8ToSJIS(mail.Body)
|
||||||
bf.WriteNullTerminatedBytes(body)
|
bf.WriteNullTerminatedBytes(body)
|
||||||
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
@@ -307,13 +308,11 @@ func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
s.mailAccIndex++
|
s.mailAccIndex++
|
||||||
|
|
||||||
itemAttached := m.AttachedItemID != 0
|
itemAttached := m.AttachedItemID != 0
|
||||||
subject := s.clientContext.StrConv.MustEncode(m.Subject)
|
|
||||||
sender := s.clientContext.StrConv.MustEncode(m.SenderName)
|
|
||||||
|
|
||||||
msg.WriteUint32(m.SenderID)
|
msg.WriteUint32(m.SenderID)
|
||||||
msg.WriteUint32(uint32(m.CreatedAt.Unix()))
|
msg.WriteUint32(uint32(m.CreatedAt.Unix()))
|
||||||
|
|
||||||
msg.WriteUint8(uint8(accIndex))
|
msg.WriteUint8(accIndex)
|
||||||
msg.WriteUint8(uint8(i))
|
msg.WriteUint8(uint8(i))
|
||||||
|
|
||||||
flags := uint8(0x00)
|
flags := uint8(0x00)
|
||||||
@@ -329,8 +328,15 @@ func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
// System message, hides ID
|
// System message, hides ID
|
||||||
// flags |= 0x04
|
// flags |= 0x04
|
||||||
|
|
||||||
if m.AttachedItemReceived {
|
// Workaround until EN mail items are patched
|
||||||
flags |= 0x08
|
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.DisableMailItems {
|
||||||
|
if itemAttached {
|
||||||
|
flags |= 0x08
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if m.AttachedItemReceived {
|
||||||
|
flags |= 0x08
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.IsGuildInvite {
|
if m.IsGuildInvite {
|
||||||
@@ -339,11 +345,10 @@ func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
msg.WriteUint8(flags)
|
msg.WriteUint8(flags)
|
||||||
msg.WriteBool(itemAttached)
|
msg.WriteBool(itemAttached)
|
||||||
msg.WriteUint8(uint8(len(subject) + 1))
|
msg.WriteUint8(16)
|
||||||
msg.WriteUint8(uint8(len(sender) + 1))
|
msg.WriteUint8(21)
|
||||||
msg.WriteNullTerminatedBytes(subject)
|
msg.WriteBytes(stringsupport.PaddedString(m.Subject, 16, true))
|
||||||
msg.WriteNullTerminatedBytes(sender)
|
msg.WriteBytes(stringsupport.PaddedString(m.SenderName, 21, true))
|
||||||
|
|
||||||
if itemAttached {
|
if itemAttached {
|
||||||
msg.WriteUint16(m.AttachedItemAmount)
|
msg.WriteUint16(m.AttachedItemAmount)
|
||||||
msg.WriteUint16(m.AttachedItemID)
|
msg.WriteUint16(m.AttachedItemID)
|
||||||
@@ -358,34 +363,22 @@ func handleMsgMhfOprtMail(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
mail, err := GetMailByID(s, s.mailList[pkt.AccIndex])
|
mail, err := GetMailByID(s, s.mailList[pkt.AccIndex])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
switch mhfpacket.OperateMailOperation(pkt.Operation) {
|
|
||||||
|
switch pkt.Operation {
|
||||||
case mhfpacket.OPERATE_MAIL_DELETE:
|
case mhfpacket.OPERATE_MAIL_DELETE:
|
||||||
err = mail.MarkDeleted(s)
|
err = mail.MarkDeleted(s)
|
||||||
if err != nil {
|
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
case mhfpacket.OPERATE_MAIL_LOCK:
|
case mhfpacket.OPERATE_MAIL_LOCK:
|
||||||
err = mail.MarkLocked(s, true)
|
err = mail.MarkLocked(s, true)
|
||||||
if err != nil {
|
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
case mhfpacket.OPERATE_MAIL_UNLOCK:
|
case mhfpacket.OPERATE_MAIL_UNLOCK:
|
||||||
err = mail.MarkLocked(s, false)
|
err = mail.MarkLocked(s, false)
|
||||||
if err != nil {
|
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
case mhfpacket.OPERATE_MAIL_ACQUIRE_ITEM:
|
case mhfpacket.OPERATE_MAIL_ACQUIRE_ITEM:
|
||||||
err = mail.MarkAcquired(s)
|
err = mail.MarkAcquired(s)
|
||||||
if err != nil {
|
}
|
||||||
doAckSimpleFail(s, pkt.AckHandle, nil)
|
|
||||||
panic(err)
|
if err != nil {
|
||||||
}
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
@@ -132,65 +131,57 @@ func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
|
|
||||||
bf := byteframe.NewByteFrame()
|
bf := byteframe.NewByteFrame()
|
||||||
|
|
||||||
bf.WriteUint32(0x00) // Unk
|
var nextID uint32
|
||||||
bf.WriteUint32(rand.Uint32()) // Partner ID?
|
s.server.db.QueryRow("SELECT nextval('rasta_id_seq')").Scan(&nextID)
|
||||||
|
|
||||||
|
bf.WriteUint32(nextID) // New MercID
|
||||||
|
bf.WriteUint32(0xDEADBEEF) // Unk
|
||||||
|
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfSaveMercenary)
|
pkt := p.(*mhfpacket.MsgMhfSaveMercenary)
|
||||||
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
|
if len(pkt.MercData) > 0 {
|
||||||
GCPValue := bf.ReadUint32()
|
s.server.db.Exec("UPDATE characters SET savemercenary=$1 WHERE id=$2", pkt.MercData, s.charID)
|
||||||
_ = bf.ReadUint32() // unk
|
|
||||||
MercDataSize := bf.ReadUint32()
|
|
||||||
MercData := bf.ReadBytes(uint(MercDataSize))
|
|
||||||
_ = bf.ReadUint32() // unk
|
|
||||||
|
|
||||||
if MercDataSize > 0 {
|
|
||||||
// the save packet has an extra null byte after its size
|
|
||||||
_, err := s.server.db.Exec("UPDATE characters SET savemercenary=$1 WHERE id=$2", MercData[:MercDataSize], s.charID)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Fatal("Failed to update savemercenary and gcp in db", zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// gcp value is always present regardless
|
|
||||||
_, err := s.server.db.Exec("UPDATE characters SET gcp=$1 WHERE id=$2", GCPValue, s.charID)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Fatal("Failed to update savemercenary and gcp in db", zap.Error(err))
|
|
||||||
}
|
}
|
||||||
|
s.server.db.Exec("UPDATE characters SET gcp=$1 WHERE id=$2", pkt.GCP, s.charID)
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfReadMercenaryW)
|
pkt := p.(*mhfpacket.MsgMhfReadMercenaryW)
|
||||||
|
if pkt.Unk0 {
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2))
|
||||||
|
return
|
||||||
|
}
|
||||||
var data []byte
|
var data []byte
|
||||||
var gcp uint32
|
var gcp uint32
|
||||||
// still has issues
|
s.server.db.QueryRow("SELECT savemercenary FROM characters WHERE id = $1", s.charID).Scan(&data)
|
||||||
err := 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 err != nil {
|
|
||||||
s.logger.Fatal("Failed to get savemercenary data from db", zap.Error(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.server.db.QueryRow("SELECT COALESCE(gcp, 0) FROM characters WHERE id = $1", s.charID).Scan(&gcp)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if len(data) == 0 {
|
|
||||||
data = []byte{0x00}
|
|
||||||
}
|
|
||||||
|
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
resp.WriteBytes(data)
|
if len(data) == 0 {
|
||||||
resp.WriteUint16(0)
|
resp.WriteBytes(make([]byte, 3))
|
||||||
|
} else {
|
||||||
|
resp.WriteBytes(data[1:])
|
||||||
|
resp.WriteUint32(0) // Unk
|
||||||
|
}
|
||||||
resp.WriteUint32(gcp)
|
resp.WriteUint32(gcp)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfReadMercenaryM(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfReadMercenaryM(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfReadMercenaryM)
|
pkt := p.(*mhfpacket.MsgMhfReadMercenaryM)
|
||||||
// accessing actual rasta data of someone else still unsure of the formatting of this
|
var data []byte
|
||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
s.server.db.QueryRow("SELECT savemercenary FROM characters WHERE id = $1", pkt.CharID).Scan(&data)
|
||||||
|
resp := byteframe.NewByteFrame()
|
||||||
|
if len(data) == 0 {
|
||||||
|
resp.WriteBool(false)
|
||||||
|
} else {
|
||||||
|
resp.WriteBytes(data[4:])
|
||||||
|
}
|
||||||
|
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfContractMercenary(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgMhfContractMercenary(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|||||||
@@ -119,12 +119,21 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
bf.WriteUint32(newSemaphore.id)
|
bf.WriteUint32(newSemaphore.id)
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
} else {
|
} else {
|
||||||
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgSysAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
|
||||||
//pkt := p.(*mhfpacket.MsgSysAcquireSemaphore)
|
pkt := p.(*mhfpacket.MsgSysAcquireSemaphore)
|
||||||
|
if sema, exists := s.server.semaphore[pkt.SemaphoreID]; exists {
|
||||||
|
sema.clients[s] = s.charID
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
bf.WriteUint32(sema.id)
|
||||||
|
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
|
||||||
|
} else {
|
||||||
|
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgSysReleaseSemaphore(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysReleaseSemaphore(s *Session, p mhfpacket.MHFPacket) {
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ package channelserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
//"erupe-ce/common/stringsupport"
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
"erupe-ce/network/mhfpacket"
|
"erupe-ce/network/mhfpacket"
|
||||||
"github.com/lib/pq"
|
"github.com/lib/pq"
|
||||||
@@ -14,16 +11,6 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func contains(s []string, str string) bool {
|
|
||||||
for _, v := range s {
|
|
||||||
if v == str {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgMhfEnumerateShop)
|
pkt := p.(*mhfpacket.MsgMhfEnumerateShop)
|
||||||
// SHOP TYPES:
|
// SHOP TYPES:
|
||||||
@@ -162,27 +149,19 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, week := time.Now().ISOWeek()
|
shopEntries, err := s.server.db.Query("SELECT itemhash,itemID,Points,TradeQuantity,rankReqLow,rankReqHigh,rankReqG,storeLevelReq,maximumQuantity,boughtQuantity,roadFloorsRequired,weeklyFatalisKills FROM normal_shop_items WHERE shoptype=$1 AND shopid=$2", pkt.ShopType, pkt.ShopID)
|
||||||
season := fmt.Sprintf("%d", week%4)
|
|
||||||
shopEntries, err := s.server.db.Query("SELECT itemhash,itemID,Points,TradeQuantity,rankReqLow,rankReqHigh,rankReqG,storeLevelReq,maximumQuantity,boughtQuantity,roadFloorsRequired,weeklyFatalisKills, COALESCE(enable_weeks, '') FROM normal_shop_items WHERE shoptype=$1 AND shopid=$2", pkt.ShopType, pkt.ShopID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
var ItemHash, entryCount int
|
var ItemHash, entryCount int
|
||||||
var itemID, Points, TradeQuantity, rankReqLow, rankReqHigh, rankReqG, storeLevelReq, maximumQuantity, boughtQuantity, roadFloorsRequired, weeklyFatalisKills, charQuantity uint16
|
var itemID, Points, TradeQuantity, rankReqLow, rankReqHigh, rankReqG, storeLevelReq, maximumQuantity, boughtQuantity, roadFloorsRequired, weeklyFatalisKills, charQuantity uint16
|
||||||
var itemWeeks string
|
|
||||||
resp := byteframe.NewByteFrame()
|
resp := byteframe.NewByteFrame()
|
||||||
resp.WriteUint32(0) // total defs
|
resp.WriteUint32(0) // total defs
|
||||||
for shopEntries.Next() {
|
for shopEntries.Next() {
|
||||||
err = shopEntries.Scan(&ItemHash, &itemID, &Points, &TradeQuantity, &rankReqLow, &rankReqHigh, &rankReqG, &storeLevelReq, &maximumQuantity, &boughtQuantity, &roadFloorsRequired, &weeklyFatalisKills, &itemWeeks)
|
err = shopEntries.Scan(&ItemHash, &itemID, &Points, &TradeQuantity, &rankReqLow, &rankReqHigh, &rankReqG, &storeLevelReq, &maximumQuantity, &boughtQuantity, &roadFloorsRequired, &weeklyFatalisKills)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(itemWeeks) > 0 && !contains(strings.Split(itemWeeks, ","), season) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.WriteUint32(uint32(ItemHash))
|
resp.WriteUint32(uint32(ItemHash))
|
||||||
resp.WriteUint16(0) // unk, always 0 in existing packets
|
resp.WriteUint16(0) // unk, always 0 in existing packets
|
||||||
resp.WriteUint16(itemID)
|
resp.WriteUint16(itemID)
|
||||||
@@ -195,13 +174,9 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
resp.WriteUint16(storeLevelReq)
|
resp.WriteUint16(storeLevelReq)
|
||||||
resp.WriteUint16(maximumQuantity)
|
resp.WriteUint16(maximumQuantity)
|
||||||
if maximumQuantity > 0 {
|
if maximumQuantity > 0 {
|
||||||
var itemWeek int
|
err = s.server.db.QueryRow("SELECT COALESCE(usedquantity,0) FROM shop_item_state WHERE itemhash=$1 AND char_id=$2", ItemHash, s.charID).Scan(&charQuantity)
|
||||||
err = s.server.db.QueryRow("SELECT COALESCE(usedquantity,0), COALESCE(week,-1) FROM shop_item_state WHERE itemhash=$1 AND char_id=$2", ItemHash, s.charID).Scan(&charQuantity, &itemWeek)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.WriteUint16(0)
|
resp.WriteUint16(0)
|
||||||
} else if pkt.ShopID == 7 && itemWeek >= 0 && itemWeek != week {
|
|
||||||
clearShopItemState(s, s.charID, uint32(ItemHash))
|
|
||||||
resp.WriteUint16(0)
|
|
||||||
} else {
|
} else {
|
||||||
resp.WriteUint16(charQuantity)
|
resp.WriteUint16(charQuantity)
|
||||||
}
|
}
|
||||||
@@ -224,7 +199,6 @@ func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {
|
||||||
_, week := time.Now().ISOWeek()
|
|
||||||
// writing out to an editable shop enumeration
|
// writing out to an editable shop enumeration
|
||||||
pkt := p.(*mhfpacket.MsgMhfAcquireExchangeShop)
|
pkt := p.(*mhfpacket.MsgMhfAcquireExchangeShop)
|
||||||
if pkt.DataSize == 10 {
|
if pkt.DataSize == 10 {
|
||||||
@@ -232,10 +206,10 @@ func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
_ = bf.ReadUint16() // unk, always 1 in examples
|
_ = bf.ReadUint16() // unk, always 1 in examples
|
||||||
itemHash := bf.ReadUint32()
|
itemHash := bf.ReadUint32()
|
||||||
buyCount := bf.ReadUint32()
|
buyCount := bf.ReadUint32()
|
||||||
_, err := s.server.db.Exec(`INSERT INTO shop_item_state (char_id, itemhash, usedquantity, week)
|
_, err := s.server.db.Exec(`INSERT INTO shop_item_state (char_id, itemhash, usedquantity)
|
||||||
VALUES ($1,$2,$3,$4) ON CONFLICT (char_id, itemhash)
|
VALUES ($1,$2,$3) ON CONFLICT (char_id, itemhash)
|
||||||
DO UPDATE SET usedquantity = shop_item_state.usedquantity + $3
|
DO UPDATE SET usedquantity = shop_item_state.usedquantity + $3
|
||||||
WHERE EXCLUDED.char_id=$1 AND EXCLUDED.itemhash=$2`, s.charID, itemHash, buyCount, week)
|
WHERE EXCLUDED.char_id=$1 AND EXCLUDED.itemhash=$2`, s.charID, itemHash, buyCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Fatal("Failed to update shop_item_state in db", zap.Error(err))
|
s.logger.Fatal("Failed to update shop_item_state in db", zap.Error(err))
|
||||||
}
|
}
|
||||||
@@ -243,13 +217,6 @@ func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
}
|
}
|
||||||
|
|
||||||
func clearShopItemState(s *Session, charId uint32, itemHash uint32) {
|
|
||||||
_, err := s.server.db.Exec(`DELETE FROM shop_item_state WHERE char_id=$1 AND itemhash=$2`, charId, itemHash)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Fatal("Failed to delete shop_item_state in db", zap.Error(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMsgMhfGetGachaPlayHistory(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgMhfGetGachaPlayHistory(s *Session, p mhfpacket.MHFPacket) {
|
||||||
// returns number of times the gacha was played, will need persistent db stuff
|
// returns number of times the gacha was played, will need persistent db stuff
|
||||||
pkt := p.(*mhfpacket.MsgMhfGetGachaPlayHistory)
|
pkt := p.(*mhfpacket.MsgMhfGetGachaPlayHistory)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package channelserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"erupe-ce/common/byteframe"
|
"erupe-ce/common/byteframe"
|
||||||
@@ -121,7 +122,7 @@ func destructEmptyStages(s *Session) {
|
|||||||
defer s.server.Unlock()
|
defer s.server.Unlock()
|
||||||
for _, stage := range s.server.stages {
|
for _, stage := range s.server.stages {
|
||||||
// Destroy empty Quest/My series/Guild stages.
|
// Destroy empty Quest/My series/Guild stages.
|
||||||
if stage.id[3:5] == "Qs" || stage.id[3:5] == "Ms" || stage.id[3:5] == "Gs" {
|
if stage.id[3:5] == "Qs" || stage.id[3:5] == "Ms" || stage.id[3:5] == "Gs" || stage.id[3:5] == "Ls" {
|
||||||
if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 {
|
if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 {
|
||||||
delete(s.server.stages, stage.id)
|
delete(s.server.stages, stage.id)
|
||||||
s.logger.Debug("Destructed stage", zap.String("stage.id", stage.id))
|
s.logger.Debug("Destructed stage", zap.String("stage.id", stage.id))
|
||||||
@@ -198,22 +199,24 @@ func handleMsgSysLeaveStage(s *Session, p mhfpacket.MHFPacket) {}
|
|||||||
func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) {
|
||||||
pkt := p.(*mhfpacket.MsgSysLockStage)
|
pkt := p.(*mhfpacket.MsgSysLockStage)
|
||||||
// TODO(Andoryuuta): What does this packet _actually_ do?
|
// TODO(Andoryuuta): What does this packet _actually_ do?
|
||||||
|
// I think this is supposed to mark a stage as no longer able to accept client reservations
|
||||||
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
|
||||||
s.reservationStage.RLock()
|
if s.reservationStage != nil {
|
||||||
defer s.reservationStage.RUnlock()
|
s.reservationStage.RLock()
|
||||||
|
defer s.reservationStage.RUnlock()
|
||||||
|
|
||||||
for charID := range s.reservationStage.reservedClientSlots {
|
for charID := range s.reservationStage.reservedClientSlots {
|
||||||
session := s.server.FindSessionByCharID(charID)
|
session := s.server.FindSessionByCharID(charID)
|
||||||
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
|
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(s.server.stages, s.reservationStage.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.server.Lock()
|
destructEmptyStages(s)
|
||||||
defer s.server.Unlock()
|
|
||||||
|
|
||||||
delete(s.server.stages, s.reservationStage.id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
|
func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
|
||||||
@@ -366,8 +369,7 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for valid stage type
|
if !strings.Contains(stage.id, pkt.StagePrefix) {
|
||||||
if sid[3:5] != "Qs" && sid[3:5] != "Ms" {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package channelserver
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"erupe-ce/common/byteframe"
|
|
||||||
"erupe-ce/network/mhfpacket"
|
"erupe-ce/network/mhfpacket"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,7 +16,8 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: pkt.BinaryType}] = pkt.RawDataPayload
|
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: pkt.BinaryType}] = pkt.RawDataPayload
|
||||||
s.server.userBinaryPartsLock.Unlock()
|
s.server.userBinaryPartsLock.Unlock()
|
||||||
|
|
||||||
err := s.server.db.QueryRow("SELECT type1 FROM user_binaries WHERE id=$1", s.charID)
|
var exists []byte
|
||||||
|
err := s.server.db.QueryRow("SELECT type2 FROM user_binaries WHERE id=$1", s.charID).Scan(&exists)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.server.db.Exec("INSERT INTO user_binaries (id) VALUES ($1)", s.charID)
|
s.server.db.Exec("INSERT INTO user_binaries (id) VALUES ($1)", s.charID)
|
||||||
}
|
}
|
||||||
@@ -39,25 +39,18 @@ func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) {
|
|||||||
s.server.userBinaryPartsLock.RLock()
|
s.server.userBinaryPartsLock.RLock()
|
||||||
defer s.server.userBinaryPartsLock.RUnlock()
|
defer s.server.userBinaryPartsLock.RUnlock()
|
||||||
data, ok := s.server.userBinaryParts[userBinaryPartID{charID: pkt.CharID, index: pkt.BinaryType}]
|
data, ok := s.server.userBinaryParts[userBinaryPartID{charID: pkt.CharID, index: pkt.BinaryType}]
|
||||||
resp := byteframe.NewByteFrame()
|
|
||||||
|
|
||||||
// If we can't get the real data, try to get it from the database.
|
// If we can't get the real data, try to get it from the database.
|
||||||
if !ok {
|
if !ok {
|
||||||
var data []byte
|
err := s.server.db.QueryRow(fmt.Sprintf("SELECT type%d FROM user_binaries WHERE id=$1", pkt.BinaryType), pkt.CharID).Scan(&data)
|
||||||
rows, _ := s.server.db.Queryx(fmt.Sprintf("SELECT type%d FROM user_binaries WHERE id=$1", pkt.BinaryType), pkt.CharID)
|
if err != nil {
|
||||||
for rows.Next() {
|
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
||||||
rows.Scan(&data)
|
} else {
|
||||||
resp.WriteBytes(data)
|
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
doAckBufFail(s, pkt.AckHandle, make([]byte, 4))
|
|
||||||
return
|
|
||||||
} else {
|
} else {
|
||||||
resp.WriteBytes(data)
|
doAckBufSucceed(s, pkt.AckHandle, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMsgSysNotifyUserBinary(s *Session, p mhfpacket.MHFPacket) {}
|
func handleMsgSysNotifyUserBinary(s *Session, p mhfpacket.MHFPacket) {}
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ type Server struct {
|
|||||||
sync.Mutex
|
sync.Mutex
|
||||||
Channels []*Server
|
Channels []*Server
|
||||||
ID uint16
|
ID uint16
|
||||||
|
IP string
|
||||||
|
Port uint16
|
||||||
logger *zap.Logger
|
logger *zap.Logger
|
||||||
db *sqlx.DB
|
db *sqlx.DB
|
||||||
erupeConfig *config.Config
|
erupeConfig *config.Config
|
||||||
@@ -196,8 +198,8 @@ func NewServer(config *Config) *Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start starts the server in a new goroutine.
|
// Start starts the server in a new goroutine.
|
||||||
func (s *Server) Start(port int) error {
|
func (s *Server) Start() error {
|
||||||
l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.Port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -295,8 +297,11 @@ func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) WorldcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
|
func (s *Server) WorldcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session, ignoredChannel *Server) {
|
||||||
for _, c := range s.Channels {
|
for _, c := range s.Channels {
|
||||||
|
if c == ignoredChannel {
|
||||||
|
continue
|
||||||
|
}
|
||||||
for _, session := range c.sessions {
|
for _, session := range c.sessions {
|
||||||
if session == ignoredSession {
|
if session == ignoredSession {
|
||||||
continue
|
continue
|
||||||
@@ -357,7 +362,7 @@ func (s *Server) BroadcastRaviente(ip uint32, port uint16, stage []byte, _type u
|
|||||||
BroadcastType: BroadcastTypeServer,
|
BroadcastType: BroadcastTypeServer,
|
||||||
MessageType: BinaryMessageTypeChat,
|
MessageType: BinaryMessageTypeChat,
|
||||||
RawDataPayload: bf.Data(),
|
RawDataPayload: bf.Data(),
|
||||||
}, nil)
|
}, nil, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) DiscordChannelSend(charName string, content string) {
|
func (s *Server) DiscordChannelSend(charName string, content string) {
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ type Session struct {
|
|||||||
stage *Stage
|
stage *Stage
|
||||||
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
|
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
|
||||||
stagePass string // Temporary storage
|
stagePass string // Temporary storage
|
||||||
|
prevGuildID uint32 // Stores the last GuildID used in InfoGuild
|
||||||
charID uint32
|
charID uint32
|
||||||
logKey []byte
|
logKey []byte
|
||||||
sessionStart int64
|
sessionStart int64
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func encodeServerInfo(config *config.Config, s *Server) []byte {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if si.IP == "" {
|
if si.IP == "" {
|
||||||
si.IP = config.HostIP
|
si.IP = config.Host
|
||||||
}
|
}
|
||||||
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4()))
|
bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4()))
|
||||||
bf.WriteUint16(16 + uint16(serverIdx))
|
bf.WriteUint16(16 + uint16(serverIdx))
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
func serverList(s *Server, w http.ResponseWriter, r *http.Request) {
|
func serverList(s *Server, w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprintf(w,
|
fmt.Fprintf(w,
|
||||||
`<?xml version="1.0"?><server_groups><group idx='0' nam='Erupe' ip='%s' port="%d"/></server_groups>`,
|
`<?xml version="1.0"?><server_groups><group idx='0' nam='Erupe' ip='%s' port="%d"/></server_groups>`,
|
||||||
s.erupeConfig.HostIP,
|
s.erupeConfig.Host,
|
||||||
s.erupeConfig.Sign.Port,
|
s.erupeConfig.Sign.Port,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ type character struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) getCharactersForUser(uid int) ([]character, error) {
|
func (s *Server) getCharactersForUser(uid int) ([]character, error) {
|
||||||
characters := []character{}
|
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", 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", uid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -126,7 +126,7 @@ func (s *Server) getFriendsForCharacters(chars []character) []members {
|
|||||||
friendQuery += " OR id="
|
friendQuery += " OR id="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
charFriends := []members{}
|
charFriends := make([]members, 0)
|
||||||
err = s.db.Select(&charFriends, friendQuery)
|
err = s.db.Select(&charFriends, friendQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
@@ -153,7 +153,7 @@ func (s *Server) getGuildmatesForCharacters(chars []character) []members {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
charGuildmates := []members{}
|
charGuildmates := make([]members, 0)
|
||||||
err = s.db.Select(&charGuildmates, "SELECT character_id AS id, c.name FROM guild_characters gc JOIN characters c ON c.id = gc.character_id WHERE guild_id=$1 AND character_id!=$2", guildID, char.ID)
|
err = s.db.Select(&charGuildmates, "SELECT character_id AS id, c.name FROM guild_characters gc JOIN characters c ON c.id = gc.character_id WHERE guild_id=$1 AND character_id!=$2", guildID, char.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
|||||||
bf.WriteUint32(0xFFFFFFFF) // login_token_number
|
bf.WriteUint32(0xFFFFFFFF) // login_token_number
|
||||||
bf.WriteBytes([]byte(token)) // login_token
|
bf.WriteBytes([]byte(token)) // login_token
|
||||||
bf.WriteUint32(uint32(time.Now().Unix())) // current time
|
bf.WriteUint32(uint32(time.Now().Unix())) // current time
|
||||||
ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.HostIP, s.server.erupeConfig.Entrance.Port), false)
|
ps.Uint8(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.Host, s.server.erupeConfig.Entrance.Port), false)
|
||||||
|
|
||||||
lastPlayed := uint32(0)
|
lastPlayed := uint32(0)
|
||||||
for _, char := range chars {
|
for _, char := range chars {
|
||||||
@@ -128,7 +128,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
|||||||
bf.WriteUint32(0x0A5197DF)
|
bf.WriteUint32(0x0A5197DF)
|
||||||
|
|
||||||
mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent
|
mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent
|
||||||
alt := false
|
alt := s.server.erupeConfig.DevModeOptions.MezFesAlt
|
||||||
if mezfes {
|
if mezfes {
|
||||||
// Start time
|
// Start time
|
||||||
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(-5 * time.Minute).Unix()))
|
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(-5 * time.Minute).Unix()))
|
||||||
@@ -136,7 +136,7 @@ func (s *Session) makeSignInResp(uid int) []byte {
|
|||||||
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(24 * time.Hour * 7).Unix()))
|
bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(24 * time.Hour * 7).Unix()))
|
||||||
bf.WriteUint8(2) // Unk
|
bf.WriteUint8(2) // Unk
|
||||||
bf.WriteUint32(20) // Single tickets
|
bf.WriteUint32(20) // Single tickets
|
||||||
bf.WriteUint32(0) // Group tickets
|
bf.WriteUint32(10) // Group tickets
|
||||||
bf.WriteUint8(8) // Stalls open
|
bf.WriteUint8(8) // Stalls open
|
||||||
bf.WriteUint8(0xA) // Unk
|
bf.WriteUint8(0xA) // Unk
|
||||||
bf.WriteUint8(0x3) // Pachinko
|
bf.WriteUint8(0x3) // Pachinko
|
||||||
|
|||||||
@@ -71,6 +71,15 @@
|
|||||||
<p class="lbl">Important Updates</p>
|
<p class="lbl">Important Updates</p>
|
||||||
</div>
|
</div>
|
||||||
<ul class="article">
|
<ul class="article">
|
||||||
|
<li>
|
||||||
|
<div class="date">2022-08-02</div>
|
||||||
|
<div class="body">
|
||||||
|
<a
|
||||||
|
href="javascript:toggleModal('openLink',"https://discord.com/channels/368424389416583169/929509970624532511/1003985850255818762");"
|
||||||
|
onclick="soundOk()">Server Update 9 Released!
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="date">2022-05-03</div>
|
<div class="date">2022-05-03</div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
var __mhf_launcher = {};
|
var __mhf_launcher = {};
|
||||||
var loginScreen = true;
|
var loginScreen = true;
|
||||||
|
var loggingIn = false;
|
||||||
var doingAuto = false;
|
var doingAuto = false;
|
||||||
var uids;
|
var uids;
|
||||||
var selectedUid;
|
var selectedUid;
|
||||||
@@ -259,6 +260,11 @@ function switchPrompt() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function doLogin(option) {
|
function doLogin(option) {
|
||||||
|
if (loggingIn) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
loggingIn = true;
|
||||||
|
}
|
||||||
let username = document.getElementById('username').value;
|
let username = document.getElementById('username').value;
|
||||||
let password = document.getElementById('password').value;
|
let password = document.getElementById('password').value;
|
||||||
if (username == '') {
|
if (username == '') {
|
||||||
@@ -289,6 +295,7 @@ function checkAuth() {
|
|||||||
setTimeout(checkAuth, 10);
|
setTimeout(checkAuth, 10);
|
||||||
return;
|
return;
|
||||||
} else if (loginResult == 'AUTH_SUCCESS') {
|
} else if (loginResult == 'AUTH_SUCCESS') {
|
||||||
|
loggingIn = false;
|
||||||
saveAccount();
|
saveAccount();
|
||||||
addLog('Connected.', 'good');
|
addLog('Connected.', 'good');
|
||||||
if (doingAuto) {
|
if (doingAuto) {
|
||||||
@@ -300,6 +307,7 @@ function checkAuth() {
|
|||||||
switchPrompt();
|
switchPrompt();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
loggingIn = false;
|
||||||
addLog('Error logging in: '+loginResult+':'+window.external.getSignResult(), 'error');
|
addLog('Error logging in: '+loginResult+':'+window.external.getSignResult(), 'error');
|
||||||
}
|
}
|
||||||
document.getElementById('processing').style.display = 'none';
|
document.getElementById('processing').style.display = 'none';
|
||||||
@@ -479,8 +487,26 @@ function doEval() {
|
|||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
document.addEventListener('keypress', function(e) {
|
document.addEventListener('keypress', function(e) {
|
||||||
if (e.key == '~') {
|
switch (e.key) {
|
||||||
document.getElementById('dev').style.display = 'block';
|
case '~':
|
||||||
|
document.getElementById('dev').style.display = 'block';
|
||||||
|
break;
|
||||||
|
case 'Enter':
|
||||||
|
if (loginScreen) {
|
||||||
|
doLogin()
|
||||||
|
} else {
|
||||||
|
soundLogin();launch()
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
if (!loginScreen) {
|
||||||
|
soundOk();charselScrollUp()
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
if (!loginScreen) {
|
||||||
|
soundOk();charselScrollDown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let unselectable = document.getElementsByClassName('unselectable');
|
let unselectable = document.getElementsByClassName('unselectable');
|
||||||
|
|||||||
Reference in New Issue
Block a user