From e5257eb6edc293b3bdabc96b43051fa2714031b4 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Mon, 13 Jan 2020 17:19:29 -0500 Subject: [PATCH 01/49] Refactor servers --- .gitignore | 3 +- channelserver/channel_server.go | 20 +- config.json | 51 +++++ config/config.go | 110 ++++++++++- entranceserver/entrance_server.go | 150 +++++++++------ entranceserver/make_resp.go | 41 +--- go.mod | 6 + go.sum | 182 ++++++++++++++++++ launcherserver/handler.go | 20 ++ launcherserver/launcher_server.go | 98 ++++++++++ .../routes.go | 64 ++---- main.go | 99 ++++++++-- signserver/dsgn_resp.go | 2 +- signserver/respid.go | 1 + signserver/session.go | 63 +++--- signserver/sign_server.go | 60 ++++-- 16 files changed, 749 insertions(+), 221 deletions(-) create mode 100644 config.json create mode 100644 launcherserver/handler.go create mode 100644 launcherserver/launcher_server.go rename launcher_server.go => launcherserver/routes.go (55%) diff --git a/.gitignore b/.gitignore index fc2d952ed..8d6aeaab4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ bin_resp/ custom_entrance_server_resp.bin dec_bin8_data_dump.bin entrance_resp_bin8_encrypted.bin -tw_server_list_resp.bin \ No newline at end of file +tw_server_list_resp.bin +Erupe.exe \ No newline at end of file diff --git a/channelserver/channel_server.go b/channelserver/channel_server.go index d7aa386d8..e70d922fc 100644 --- a/channelserver/channel_server.go +++ b/channelserver/channel_server.go @@ -5,21 +5,27 @@ import ( "fmt" "net" "sync" + + "github.com/Andoryuuta/Erupe/config" + "go.uber.org/zap" ) // Config struct allows configuring the server. type Config struct { - DB *sql.DB - ListenAddr string + Logger *zap.Logger + DB *sql.DB + ErupeConfig *config.Config } // Server is a MHF channel server. type Server struct { sync.Mutex + logger *zap.Logger + db *sql.DB + erupeConfig *config.Config acceptConns chan net.Conn deleteConns chan net.Conn sessions map[net.Conn]*Session - db *sql.DB listenAddr string listener net.Listener // Listener that is created when Server.Start is called. } @@ -27,23 +33,23 @@ type Server struct { // NewServer creates a new Server type. func NewServer(config *Config) *Server { s := &Server{ + logger: config.Logger, + db: config.DB, + erupeConfig: config.ErupeConfig, acceptConns: make(chan net.Conn), deleteConns: make(chan net.Conn), sessions: make(map[net.Conn]*Session), - db: config.DB, - listenAddr: config.ListenAddr, } return s } // Start starts the server in a new goroutine. func (s *Server) Start() error { - l, err := net.Listen("tcp", s.listenAddr) + l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.erupeConfig.Channel.Port)) if err != nil { return err } s.listener = l - //defer l.Close() go s.acceptClients() go s.manageSessions() diff --git a/config.json b/config.json new file mode 100644 index 000000000..cfb58cd45 --- /dev/null +++ b/config.json @@ -0,0 +1,51 @@ +{ + "host_ip": "127.0.0.1", + + "database": { + "host": "localhost", + "port": 5432, + "user": "postgres", + "password": "admin", + "database": "erupe" + }, + "launcher": { + "port": 80 + }, + "sign": { + "port": 53312 + }, + "channel": { + "port": 54001 + }, + "entrance": { + "port": 53310, + "entries": [ + { + "name": "AErupe server 1", + "ip": "127.0.0.1", + "unk2": 0, + "type": 1, + "season": 0, + "unk6": 3, + "allowedclientflags": "4096", + "channels": [ + { + "port": 54001, + "MaxPlayers": 100, + "CurrentPlayers": 0, + "Unk4": 0, + "Unk5": 0, + "Unk6": 0, + "Unk7": 0, + "Unk8": 0, + "Unk9": 0, + "Unk10": 319, + "Unk11": 248, + "Unk12": 159, + "Unk13": 12345 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/config/config.go b/config/config.go index 1cb7cd0fc..5886386c6 100644 --- a/config/config.go +++ b/config/config.go @@ -1,25 +1,119 @@ package config -import "github.com/BurntSushi/toml" +import ( + "log" + "net" -// Config holds the configuration settings from the toml file. + "github.com/spf13/viper" +) + +// Config holds the global server-wide config. type Config struct { - DB Database `toml:"database"` + HostIP string `mapstructure:"host_ip"` + + Database Database + Launcher Launcher + Sign Sign + Channel Channel + Entrance Entrance } // Database holds the postgres database config. type Database struct { - Server string - Port int + Host string + Port int + User string + Password string + Database string +} + +// Launcher holds the launcher server config. +type Launcher struct { + Port int +} + +// Sign holds the sign server config. +type Sign struct { + Port int +} + +// Channel holds the channel server config. +type Channel struct { + Port int +} + +// Entrance holds the entrance server config. +type Entrance struct { + Port uint16 + Entries []EntranceServerInfo +} + +// EntranceServerInfo represents an entry in the serverlist. +type EntranceServerInfo struct { + IP string + Unk2 uint16 + Type uint8 // Server type. 0=?, 1=open, 2=cities, 3=newbie, 4=bar + Season uint8 // Server activity. 0 = green, 1 = orange, 2 = blue + Unk6 uint8 // Something to do with server recommendation on 0, 3, and 5. + Name string // Server name, 66 byte null terminated Shift-JIS(JP) or Big5(TW). + + // 4096(PC, PS3/PS4)?, 8258(PC, PS3/PS4)?, 8192 == nothing? + // THIS ONLY EXISTS IF Binary8Header.type == "SV2", NOT "SVR"! + AllowedClientFlags uint32 + + Channels []EntranceChannelInfo +} + +// EntranceChannelInfo represents an entry in a server's channel list. +type EntranceChannelInfo struct { + Port uint16 + MaxPlayers uint16 + CurrentPlayers uint16 + Unk4 uint16 + Unk5 uint16 + Unk6 uint16 + Unk7 uint16 + Unk8 uint16 + Unk9 uint16 + Unk10 uint16 + Unk11 uint16 + Unk12 uint16 + Unk13 uint16 +} + +// getOutboundIP4 gets the preferred outbound ip4 of this machine +// From https://stackoverflow.com/a/37382208 +func getOutboundIP4() net.IP { + conn, err := net.Dial("udp4", "8.8.8.8:80") + if err != nil { + log.Fatal(err) + } + defer conn.Close() + + localAddr := conn.LocalAddr().(*net.UDPAddr) + + return localAddr.IP.To4() } // LoadConfig loads the given config toml file. -func LoadConfig(filepath string) (*Config, error) { - c := &Config{} - _, err := toml.DecodeFile(filepath, c) +func LoadConfig() (*Config, error) { + viper.SetConfigName("config") + viper.AddConfigPath(".") + + err := viper.ReadInConfig() if err != nil { return nil, err } + c := &Config{} + err = viper.Unmarshal(c) + if err != nil { + return nil, err + } + + if c.HostIP == "" { + c.HostIP = getOutboundIP4().To4().String() + } + return c, nil } diff --git a/entranceserver/entrance_server.go b/entranceserver/entrance_server.go index e43ede422..8ac3a9846 100644 --- a/entranceserver/entrance_server.go +++ b/entranceserver/entrance_server.go @@ -1,80 +1,120 @@ package entranceserver import ( + "database/sql" "encoding/hex" "fmt" "io" "net" + "sync" + "github.com/Andoryuuta/Erupe/config" "github.com/Andoryuuta/Erupe/network" + "go.uber.org/zap" ) -func handleEntranceServerConnection(conn net.Conn) { +// Server is a MHF entrance server. +type Server struct { + sync.Mutex + logger *zap.Logger + erupeConfig *config.Config + db *sql.DB + listener net.Listener + isShuttingDown bool +} + +// Config struct allows configuring the server. +type Config struct { + Logger *zap.Logger + DB *sql.DB + ErupeConfig *config.Config +} + +// NewServer creates a new Server type. +func NewServer(config *Config) *Server { + s := &Server{ + logger: config.Logger, + erupeConfig: config.ErupeConfig, + db: config.DB, + } + return s +} + +// Start starts the server in a new goroutine. +func (s *Server) Start() error { + + l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.erupeConfig.Entrance.Port)) + if err != nil { + return err + } + + s.listener = l + + go s.acceptClients() + + return nil +} + +// Shutdown exits the server gracefully. +func (s *Server) Shutdown() { + s.logger.Debug("Shutting down") + + s.Lock() + s.isShuttingDown = true + s.Unlock() + + // This will cause the acceptor goroutine to error and exit gracefully. + s.listener.Close() +} + +//acceptClients handles accepting new clients in a loop. +func (s *Server) acceptClients() { + for { + conn, err := s.listener.Accept() + if err != nil { + // Check if we are shutting down and exit gracefully if so. + s.Lock() + shutdown := s.isShuttingDown + s.Unlock() + + if shutdown { + break + } else { + continue + } + } + + // Start a new goroutine for the connection so that we don't block other incoming connections. + go s.handleEntranceServerConnection(conn) + } +} + +func (s *Server) handleEntranceServerConnection(conn net.Conn) { // Client initalizes the connection with a one-time buffer of 8 NULL bytes. nullInit := make([]byte, 8) n, err := io.ReadFull(conn, nullInit) if err != nil { - fmt.Println(err) + s.logger.Warn("Failed to read 8 NULL init", zap.Error(err)) return } else if n != len(nullInit) { - fmt.Println("io.ReadFull couldn't read the full 8 byte init.") + s.logger.Warn("io.ReadFull couldn't read the full 8 byte init.") return } + // Create a new encrypted connection handler and read a packet from it. cc := network.NewCryptConn(conn) - for { - pkt, err := cc.ReadPacket() - if err != nil { - return - } - - fmt.Printf("Got entrance server command:\n%s\n", hex.Dump(pkt)) - - data := makeResp([]ServerInfo{ - ServerInfo{ - IP: net.ParseIP("127.0.0.1"), - Unk2: 0, - Type: 1, - Season: 0, - Unk6: 3, - Name: "AErupe Server in Go! @localhost", - AllowedClientFlags: 4096, - Channels: []ChannelInfo{ - ChannelInfo{ - Port: 54001, - MaxPlayers: 100, - CurrentPlayers: 0, - Unk4: 0, - Unk5: 0, - Unk6: 0, - Unk7: 0, - Unk8: 0, - Unk9: 0, - Unk10: 319, - Unk11: 248, - Unk12: 159, - Unk13: 12345, - }, - }, - }, - }) - cc.SendPacket(data) - - } -} - -func DoEntranceServer(listenAddr string) { - l, err := net.Listen("tcp", listenAddr) + pkt, err := cc.ReadPacket() if err != nil { - panic(err) + s.logger.Warn("Error reading packet", zap.Error(err)) + return } - defer l.Close() - for { - conn, err := l.Accept() - if err != nil { - panic(err) - } - go handleEntranceServerConnection(conn) - } + s.logger.Debug("Got entrance server command:\n", zap.String("raw", hex.Dump(pkt))) + + data := makeResp(s.erupeConfig.Entrance.Entries) + cc.SendPacket(data) + + // Close because we only need to send the response once. + // Any further requests from the client will come from a new connection. + conn.Close() } diff --git a/entranceserver/make_resp.go b/entranceserver/make_resp.go index bd3ff98e8..73ac12ea6 100644 --- a/entranceserver/make_resp.go +++ b/entranceserver/make_resp.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "net" + "github.com/Andoryuuta/Erupe/config" "github.com/Andoryuuta/byteframe" ) @@ -16,45 +17,11 @@ func paddedString(x string, size uint) []byte { return out } -// ServerInfo represents an entry in the serverlist. -type ServerInfo struct { - IP net.IP - Unk2 uint16 - Type uint8 // Server type. 0=?, 1=open, 2=cities, 3=newbie, 4=bar - Season uint8 // Server activity. 0 = green, 1 = orange, 2 = blue - Unk6 uint8 // Something to do with server recommendation on 0, 3, and 5. - Name string // Server name, 66 byte null terminated Shift-JIS. - - // 4096(PC, PS3/PS4)?, 8258(PC, PS3/PS4)?, 8192 == nothing? - // THIS ONLY EXISTS IF Binary8Header.type == "SV2", NOT "SVR"! - AllowedClientFlags uint32 - - Channels []ChannelInfo -} - -// ChannelInfo represents an entry in a server's channel list. -type ChannelInfo struct { - Port uint16 - //ChannelIndex uint16 - MaxPlayers uint16 - CurrentPlayers uint16 - Unk4 uint16 - Unk5 uint16 - Unk6 uint16 - Unk7 uint16 - Unk8 uint16 - Unk9 uint16 - Unk10 uint16 - Unk11 uint16 - Unk12 uint16 - Unk13 uint16 -} - -func encodeServerInfo(serverInfos []ServerInfo) []byte { +func encodeServerInfo(serverInfos []config.EntranceServerInfo) []byte { bf := byteframe.NewByteFrame() for serverIdx, si := range serverInfos { - bf.WriteUint32(binary.LittleEndian.Uint32(si.IP.To4())) + bf.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(si.IP).To4())) bf.WriteUint16(16 + uint16(serverIdx)) bf.WriteUint16(si.Unk2) bf.WriteUint16(uint16(len(si.Channels))) @@ -103,7 +70,7 @@ func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byt return bf.Data() } -func makeResp(servers []ServerInfo) []byte { +func makeResp(servers []config.EntranceServerInfo) []byte { rawServerData := encodeServerInfo(servers) bf := byteframe.NewByteFrame() diff --git a/go.mod b/go.mod index 7148e08f1..7f512f3a0 100644 --- a/go.mod +++ b/go.mod @@ -8,4 +8,10 @@ require ( github.com/gorilla/handlers v1.4.2 github.com/gorilla/mux v1.7.3 github.com/lib/pq v1.3.0 + github.com/spf13/viper v1.6.1 + go.uber.org/atomic v1.5.1 // indirect + go.uber.org/multierr v1.4.0 // indirect + go.uber.org/zap v1.13.0 + golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect + golang.org/x/tools v0.0.0-20200110213125-a7a6caa82ab2 // indirect ) diff --git a/go.sum b/go.sum index d300d1197..b236b54e0 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,192 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0 h1:2pVgen9rh18IxSWxOa80bObcpyfrS6d5bJtZeCUN7rY= github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0/go.mod h1:koVyx+gN3TfE70rpOidywETVODk87304YpwW69Y27J4= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk= +github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.5.1 h1:rsqfU5vBkVknbhUGbAUwQKR2H4ItV8tjJ+6kJX4cxHM= +go.uber.org/atomic v1.5.1/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.4.0 h1:f3WCSC2KzAcBXGATIxAB1E2XuCpNU255wNKZ505qi3E= +go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200110213125-a7a6caa82ab2 h1:V9r/14uGBqLgNlHRYWdVqjMdWkcOHnE2KG8DwVqQSEc= +golang.org/x/tools v0.0.0-20200110213125-a7a6caa82ab2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/launcherserver/handler.go b/launcherserver/handler.go new file mode 100644 index 000000000..d1cad4f3c --- /dev/null +++ b/launcherserver/handler.go @@ -0,0 +1,20 @@ +package launcherserver + +import ( + "net/http" +) + +// ServerHandler is a handler function akin to http.Handler's ServeHTTP, +// but has an additional *Server argument. +type ServerHandler func(*Server, http.ResponseWriter, *http.Request) + +// ServerHandlerFunc is a small type that implements http.Handler and +// wraps a calling ServerHandler with a *Server argument. +type ServerHandlerFunc struct { + server *Server + f ServerHandler +} + +func (shf ServerHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { + shf.f(shf.server, w, r) +} diff --git a/launcherserver/launcher_server.go b/launcherserver/launcher_server.go new file mode 100644 index 000000000..8d2e39adb --- /dev/null +++ b/launcherserver/launcher_server.go @@ -0,0 +1,98 @@ +package launcherserver + +import ( + "context" + "database/sql" + "fmt" + "net/http" + "os" + "sync" + "time" + + "github.com/Andoryuuta/Erupe/config" + "github.com/gorilla/handlers" + "github.com/gorilla/mux" + "go.uber.org/zap" +) + +// Config struct allows configuring the server. +type Config struct { + Logger *zap.Logger + DB *sql.DB + ErupeConfig *config.Config + UseOriginalLauncherFiles bool +} + +// Server is the MHF launcher HTTP server. +type Server struct { + sync.Mutex + logger *zap.Logger + erupeConfig *config.Config + db *sql.DB + httpServer *http.Server + useOriginalLauncherFiles bool + isShuttingDown bool +} + +// NewServer creates a new Server type. +func NewServer(config *Config) *Server { + s := &Server{ + logger: config.Logger, + erupeConfig: config.ErupeConfig, + db: config.DB, + useOriginalLauncherFiles: config.UseOriginalLauncherFiles, + httpServer: &http.Server{}, + } + return s +} + +// Start starts the server in a new goroutine. +func (s *Server) Start() error { + // Set up the routes responsible for serving the launcher HTML, serverlist, unique name check, and JP auth. + r := mux.NewRouter() + + // Universal serverlist.xml route + s.setupServerlistRoutes(r) + + // Change the launcher HTML routes if we are using the custom launcher instead of the original. + if s.useOriginalLauncherFiles { + s.setupOriginalLauncherRotues(r) + } else { + s.setupCustomLauncherRotues(r) + } + + s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.Launcher.Port) + s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, r) + + serveError := make(chan error, 1) + go func() { + if err := s.httpServer.ListenAndServe(); err != nil { + // Send error if any. + serveError <- err + } + }() + + // Get the error from calling ListenAndServe, otherwise assume it's good after 250 milliseconds. + select { + case err := <-serveError: + return err + case <-time.After(250 * time.Millisecond): + return nil + } +} + +// Shutdown exits the server gracefully. +func (s *Server) Shutdown() { + s.logger.Debug("Shutting down") + + s.Lock() + s.isShuttingDown = true + s.Unlock() + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := s.httpServer.Shutdown(ctx); err != nil { + // Just warn because we are shutting down the server anyway. + s.logger.Warn("Got error on httpServer shutdown", zap.Error(err)) + } +} diff --git a/launcher_server.go b/launcherserver/routes.go similarity index 55% rename from launcher_server.go rename to launcherserver/routes.go index 9bc81d785..bcdce830b 100644 --- a/launcher_server.go +++ b/launcherserver/routes.go @@ -1,36 +1,20 @@ -package main +package launcherserver import ( "fmt" "html" - "log" - "net" "net/http" - "os" - "github.com/gorilla/handlers" "github.com/gorilla/mux" //"github.com/julienschmidt/httprouter" ) -// GetOutboundIP4 gets the preferred outbound ip4 of this machine -// From https://stackoverflow.com/a/37382208 -func GetOutboundIP4() net.IP { - conn, err := net.Dial("udp4", "8.8.8.8:80") - if err != nil { - log.Fatal(err) - } - defer conn.Close() - - localAddr := conn.LocalAddr().(*net.UDPAddr) - - return localAddr.IP.To4() -} - -func serverList(w http.ResponseWriter, r *http.Request) { - // TODO(Andoryuuta): Redo launcher server to allow configurable serverlist host and port. - fmt.Fprintf(w, ``, GetOutboundIP4().String()) - +func serverList(s *Server, w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, + ``, + s.erupeConfig.HostIP, + s.erupeConfig.Entrance.Port, + ) } func serverUniqueName(w http.ResponseWriter, r *http.Request) { @@ -39,8 +23,7 @@ func serverUniqueName(w http.ResponseWriter, r *http.Request) { } func jpLogin(w http.ResponseWriter, r *http.Request) { - // HACK: Return the given password back as the `skey` to defer the login logic to the sign server. - + // HACK(Andoryuuta): Return the given password back as the `skey` to defer the login logic to the sign server. resultJSON := fmt.Sprintf(`{"result": "Ok", "skey": "%s", "code": "000", "msg": ""}`, r.FormValue("pw")) fmt.Fprintf(w, @@ -58,18 +41,18 @@ func jpLogin(w http.ResponseWriter, r *http.Request) { } -func setupServerlistRoutes(r *mux.Router) { +func (s *Server) setupServerlistRoutes(r *mux.Router) { // TW twServerList := r.Host("mhf-n.capcom.com.tw").Subrouter() twServerList.HandleFunc("/server/unique.php", serverUniqueName) // Name checking is also done on this host. - twServerList.HandleFunc("/server/serverlist.xml", serverList) + twServerList.Handle("/server/serverlist.xml", ServerHandlerFunc{s, serverList}) // JP jpServerList := r.Host("srv-mhf.capcom-networks.jp").Subrouter() - jpServerList.HandleFunc("/serverlist.xml", serverList) + jpServerList.Handle("/serverlist.xml", ServerHandlerFunc{s, serverList}) } -func setupOriginalLauncherRotues(r *mux.Router) { +func (s *Server) setupOriginalLauncherRotues(r *mux.Router) { // TW twMain := r.Host("mhfg.capcom.com.tw").Subrouter() twMain.PathPrefix("/").Handler(http.FileServer(http.Dir("./www/tw/"))) @@ -85,7 +68,7 @@ func setupOriginalLauncherRotues(r *mux.Router) { } -func setupCustomLauncherRotues(r *mux.Router) { +func (s *Server) setupCustomLauncherRotues(r *mux.Router) { // TW twMain := r.Host("mhfg.capcom.com.tw").Subrouter() twMain.PathPrefix("/g6_launcher/").Handler(http.StripPrefix("/g6_launcher/", http.FileServer(http.Dir("./www/erupe/")))) @@ -94,24 +77,3 @@ func setupCustomLauncherRotues(r *mux.Router) { jpMain := r.Host("cog-members.mhf-z.jp").Subrouter() jpMain.PathPrefix("/launcher/").Handler(http.StripPrefix("/launcher/", http.FileServer(http.Dir("./www/erupe")))) } - -// serveLauncherHTML is responsible for serving the launcher HTML, serverlist, unique name check, and JP auth. -func serveLauncherHTML(listenAddr string, useOriginalLauncher bool) { - r := mux.NewRouter() - - setupServerlistRoutes(r) - - if useOriginalLauncher { - setupOriginalLauncherRotues(r) - } else { - setupCustomLauncherRotues(r) - } - /* - http.ListenAndServe(listenAddr, handlers.CustomLoggingHandler(os.Stdout, r, func(writer io.Writer, params handlers.LogFormatterParams) { - dump, _ := httputil.DumpRequest(params.Request, true) - writer.Write(dump) - })) - */ - - http.ListenAndServe(listenAddr, handlers.LoggingHandler(os.Stdout, r)) -} diff --git a/main.go b/main.go index ed6d0d43c..ae16f0977 100644 --- a/main.go +++ b/main.go @@ -3,58 +3,117 @@ package main import ( "database/sql" "fmt" - "time" + "os" + "os/signal" + "syscall" "github.com/Andoryuuta/Erupe/channelserver" - "github.com/Andoryuuta/Erupe/signserver" + "github.com/Andoryuuta/Erupe/config" "github.com/Andoryuuta/Erupe/entranceserver" + "github.com/Andoryuuta/Erupe/launcherserver" + "github.com/Andoryuuta/Erupe/signserver" _ "github.com/lib/pq" + "go.uber.org/zap" ) func main() { - fmt.Println("Starting!") + zapLogger, _ := zap.NewDevelopment() + defer zapLogger.Sync() + logger := zapLogger.Named("main") - // Load the config.toml configuration. - // TODO(Andoryuuta): implement config loading. + logger.Info("Starting Erupe") + + // Load the configuration. + erupeConfig, err := config.LoadConfig() + if err != nil { + logger.Fatal("Failed to load config", zap.Error(err)) + } // Create the postgres DB pool. - db, err := sql.Open("postgres", "host=localhost port=5432 user=postgres password=admin dbname=erupe sslmode=disable") + connectString := fmt.Sprintf( + "host=%s port=%d user=%s password=%s dbname= %s sslmode=disable", + erupeConfig.Database.Host, + erupeConfig.Database.Port, + erupeConfig.Database.User, + erupeConfig.Database.Password, + erupeConfig.Database.Database, + ) + + db, err := sql.Open("postgres", connectString) if err != nil { - panic(err) + logger.Fatal("Failed to open sql database", zap.Error(err)) } // Test the DB connection. err = db.Ping() if err != nil { - panic(err) + logger.Fatal("Failed to ping database", zap.Error(err)) } + logger.Info("Connected to database.") - // Finally start our server(s). - go serveLauncherHTML(":80", false) - go entranceserver.DoEntranceServer(":53310") + // Now start our server(s). + // Launcher HTTP server. + launcherServer := launcherserver.NewServer( + &launcherserver.Config{ + Logger: logger.Named("launcher"), + ErupeConfig: erupeConfig, + DB: db, + UseOriginalLauncherFiles: false, + }) + err = launcherServer.Start() + if err != nil { + logger.Fatal("Failed to start launcher server", zap.Error(err)) + } + logger.Info("Started launcher server.") + + // Entrance server. + entranceServer := entranceserver.NewServer( + &entranceserver.Config{ + Logger: logger.Named("entrance"), + ErupeConfig: erupeConfig, + DB: db, + }) + err = entranceServer.Start() + if err != nil { + logger.Fatal("Failed to start entrance server", zap.Error(err)) + } + logger.Info("Started entrance server.") + + // Sign server. signServer := signserver.NewServer( &signserver.Config{ - DB: db, - ListenAddr: ":53312", + Logger: logger.Named("sign"), + ErupeConfig: erupeConfig, + DB: db, }) err = signServer.Start() if err != nil { - panic(err) + logger.Fatal("Failed to start sign server", zap.Error(err)) } + logger.Info("Started sign server.") + // Channel Server channelServer := channelserver.NewServer( &channelserver.Config{ - DB: db, - ListenAddr: ":54001", + Logger: logger.Named("channel"), + ErupeConfig: erupeConfig, + DB: db, }) err = channelServer.Start() if err != nil { - panic(err) + logger.Fatal("Failed to start channel server", zap.Error(err)) } + logger.Info("Started channel server.") - for { - time.Sleep(1 * time.Second) - } + // Wait for exit or interrupt with ctrl+C. + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + <-c + + logger.Info("Trying to shutdown gracefully.") + signServer.Shutdown() + entranceServer.Shutdown() + launcherServer.Shutdown() } diff --git a/signserver/dsgn_resp.go b/signserver/dsgn_resp.go index 000d68c91..593368eca 100644 --- a/signserver/dsgn_resp.go +++ b/signserver/dsgn_resp.go @@ -27,7 +27,7 @@ func makeSignInFailureResp(respID RespID) []byte { return bf.Data() } -func makeSignInResp(username string) []byte { +func (session *Session) makeSignInResp(username string) []byte { bf := byteframe.NewByteFrame() // delete me: diff --git a/signserver/respid.go b/signserver/respid.go index a55aed14a..47e7683d4 100644 --- a/signserver/respid.go +++ b/signserver/respid.go @@ -1,6 +1,7 @@ package signserver //revive:disable + type RespID uint16 //go:generate stringer -type=RespID diff --git a/signserver/session.go b/signserver/session.go index 3eebeac9a..c749a79d1 100644 --- a/signserver/session.go +++ b/signserver/session.go @@ -3,104 +3,115 @@ package signserver import ( "database/sql" "encoding/hex" - "fmt" "net" "sync" "github.com/Andoryuuta/Erupe/network" "github.com/Andoryuuta/byteframe" + "go.uber.org/zap" ) // Session holds state for the sign server connection. type Session struct { sync.Mutex + logger *zap.Logger sid int server *Server rawConn *net.Conn cryptConn *network.CryptConn } -func (session *Session) fail() { - session.server.Lock() - delete(session.server.sessions, session.sid) - session.server.Unlock() +func (s *Session) fail() { + s.server.Lock() + delete(s.server.sessions, s.sid) + s.server.Unlock() } -func (session *Session) work() { +func (s *Session) work() { for { - pkt, err := session.cryptConn.ReadPacket() + pkt, err := s.cryptConn.ReadPacket() if err != nil { - session.fail() + s.fail() return } - err = session.handlePacket(pkt) + err = s.handlePacket(pkt) if err != nil { - session.fail() + s.fail() return } } } -func (session *Session) handlePacket(pkt []byte) error { +func (s *Session) handlePacket(pkt []byte) error { + sugar := s.logger.Sugar() + bf := byteframe.NewByteFrameFromBytes(pkt) reqType := string(bf.ReadNullTerminatedBytes()) switch reqType { case "DLTSKEYSIGN:100": fallthrough case "DSGN:100": - session.handleDSGNRequest(bf) - break + err := s.handleDSGNRequest(bf) + if err != nil { + return nil + } case "DELETE:100": loginTokenString := string(bf.ReadNullTerminatedBytes()) _ = loginTokenString characterID := bf.ReadUint32() - fmt.Printf("Got delete request for character ID: %v\n", characterID) - fmt.Printf("remaining unknown data:\n%s\n", hex.Dump(bf.DataFromCurrent())) + sugar.Infof("Got delete request for character ID: %v\n", characterID) + sugar.Infof("remaining unknown data:\n%s\n", hex.Dump(bf.DataFromCurrent())) default: - fmt.Printf("Got unknown request type %s, data:\n%s\n", reqType, hex.Dump(bf.DataFromCurrent())) + sugar.Infof("Got unknown request type %s, data:\n%s\n", reqType, hex.Dump(bf.DataFromCurrent())) } return nil } -func (session *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error { +func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error { + reqUsername := string(bf.ReadNullTerminatedBytes()) reqPassword := string(bf.ReadNullTerminatedBytes()) reqUnk := string(bf.ReadNullTerminatedBytes()) - fmt.Printf("Got sign in request:\n\tUsername: %s\n\tPassword %s\n\tUnk: %s\n", reqUsername, reqPassword, reqUnk) + + s.server.logger.Info( + "Got sign in request", + zap.String("reqUsername", reqUsername), + zap.String("reqPassword", reqPassword), + zap.String("reqUnk", reqUnk), + ) // TODO(Andoryuuta): remove plaintext password storage if this ever becomes more than a toy project. var ( id int password string ) - err := session.server.db.QueryRow("SELECT id, password FROM users WHERE username = $1", reqUsername).Scan(&id, &password) + err := s.server.db.QueryRow("SELECT id, password FROM users WHERE username = $1", reqUsername).Scan(&id, &password) var serverRespBytes []byte switch { case err == sql.ErrNoRows: - fmt.Printf("No rows for username %s\n", reqUsername) + s.logger.Info("Account not found", zap.String("reqUsername", reqUsername)) serverRespBytes = makeSignInFailureResp(SIGN_EAUTH) break case err != nil: serverRespBytes = makeSignInFailureResp(SIGN_EABORT) - fmt.Println("Got error on SQL query!") - fmt.Println(err) + s.logger.Warn("Got error on SQL query", zap.Error(err)) break default: if reqPassword == password { - fmt.Println("Passwords match!") - serverRespBytes = makeSignInResp(reqUsername) + s.logger.Info("Passwords match!") + serverRespBytes = s.makeSignInResp(reqUsername) } else { - fmt.Println("Passwords don't match!") + s.logger.Info("Passwords don't match!") serverRespBytes = makeSignInFailureResp(SIGN_EPASS) } } - err = session.cryptConn.SendPacket(serverRespBytes) + err = s.cryptConn.SendPacket(serverRespBytes) if err != nil { return err } diff --git a/signserver/sign_server.go b/signserver/sign_server.go index 6654f16da..de6b60d5d 100644 --- a/signserver/sign_server.go +++ b/signserver/sign_server.go @@ -7,55 +7,81 @@ import ( "net" "sync" + "github.com/Andoryuuta/Erupe/config" "github.com/Andoryuuta/Erupe/network" + "go.uber.org/zap" ) // Config struct allows configuring the server. type Config struct { - DB *sql.DB - ListenAddr string + Logger *zap.Logger + DB *sql.DB + ErupeConfig *config.Config } // Server is a MHF sign server. type Server struct { sync.Mutex - sid int - sessions map[int]*Session - db *sql.DB - listenAddr string - listener net.Listener + logger *zap.Logger + erupeConfig *config.Config + sid int + sessions map[int]*Session + db *sql.DB + listener net.Listener + isShuttingDown bool } // NewServer creates a new Server type. func NewServer(config *Config) *Server { s := &Server{ - sid: 0, - sessions: make(map[int]*Session), - db: config.DB, - listenAddr: config.ListenAddr, + logger: config.Logger, + erupeConfig: config.ErupeConfig, + sid: 0, + sessions: make(map[int]*Session), + db: config.DB, } return s } // Start starts the server in a new goroutine. func (s *Server) Start() error { - l, err := net.Listen("tcp", s.listenAddr) + l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.erupeConfig.Sign.Port)) if err != nil { return err } s.listener = l - //defer l.Close() go s.acceptClients() return nil } +// Shutdown exits the server gracefully. +func (s *Server) Shutdown() { + s.logger.Debug("Shutting down") + + s.Lock() + s.isShuttingDown = true + s.Unlock() + + // This will cause the acceptor goroutine to error and exit gracefully. + s.listener.Close() +} + func (s *Server) acceptClients() { for { conn, err := s.listener.Accept() if err != nil { - panic(err) + // Check if we are shutting down and exit gracefully if so. + s.Lock() + shutdown := s.isShuttingDown + s.Unlock() + + if shutdown { + break + } else { + panic(err) + } } go s.handleConnection(s.sid, conn) @@ -64,7 +90,7 @@ func (s *Server) acceptClients() { } func (s *Server) handleConnection(sid int, conn net.Conn) { - fmt.Println("Got connection to sign server") + s.logger.Info("Got connection to sign server", zap.String("remoteaddr", conn.RemoteAddr().String())) // Client initalizes the connection with a one-time buffer of 8 NULL bytes. nullInit := make([]byte, 8) @@ -75,15 +101,19 @@ func (s *Server) handleConnection(sid int, conn net.Conn) { return } + // Create a new session. session := &Session{ + logger: s.logger, server: s, rawConn: &conn, cryptConn: network.NewCryptConn(conn), } + // Add the session to the server's sessions map. s.Lock() s.sessions[sid] = session s.Unlock() + // Do the session's work. session.work() } From 30219b8bcf087df1087460bcd1617bc5100fc01b Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Mon, 13 Jan 2020 17:32:49 -0500 Subject: [PATCH 02/49] Change project dir structure --- main.go | 8 ++++---- {channelserver => server/channelserver}/channel_server.go | 0 {channelserver => server/channelserver}/handler_table.go | 0 {channelserver => server/channelserver}/handlers.go | 0 {channelserver => server/channelserver}/session.go | 0 {entranceserver => server/entranceserver}/crypto.go | 0 {entranceserver => server/entranceserver}/crypto_test.go | 0 .../entranceserver}/entrance_server.go | 0 {entranceserver => server/entranceserver}/make_resp.go | 0 {launcherserver => server/launcherserver}/handler.go | 0 .../launcherserver}/launcher_server.go | 0 {launcherserver => server/launcherserver}/routes.go | 0 {signserver => server/signserver}/dsgn_resp.go | 0 {signserver => server/signserver}/respid.go | 0 {signserver => server/signserver}/session.go | 0 {signserver => server/signserver}/sign_server.go | 0 16 files changed, 4 insertions(+), 4 deletions(-) rename {channelserver => server/channelserver}/channel_server.go (100%) rename {channelserver => server/channelserver}/handler_table.go (100%) rename {channelserver => server/channelserver}/handlers.go (100%) rename {channelserver => server/channelserver}/session.go (100%) rename {entranceserver => server/entranceserver}/crypto.go (100%) rename {entranceserver => server/entranceserver}/crypto_test.go (100%) rename {entranceserver => server/entranceserver}/entrance_server.go (100%) rename {entranceserver => server/entranceserver}/make_resp.go (100%) rename {launcherserver => server/launcherserver}/handler.go (100%) rename {launcherserver => server/launcherserver}/launcher_server.go (100%) rename {launcherserver => server/launcherserver}/routes.go (100%) rename {signserver => server/signserver}/dsgn_resp.go (100%) rename {signserver => server/signserver}/respid.go (100%) rename {signserver => server/signserver}/session.go (100%) rename {signserver => server/signserver}/sign_server.go (100%) diff --git a/main.go b/main.go index ae16f0977..f197959bf 100644 --- a/main.go +++ b/main.go @@ -7,11 +7,11 @@ import ( "os/signal" "syscall" - "github.com/Andoryuuta/Erupe/channelserver" "github.com/Andoryuuta/Erupe/config" - "github.com/Andoryuuta/Erupe/entranceserver" - "github.com/Andoryuuta/Erupe/launcherserver" - "github.com/Andoryuuta/Erupe/signserver" + "github.com/Andoryuuta/Erupe/server/channelserver" + "github.com/Andoryuuta/Erupe/server/entranceserver" + "github.com/Andoryuuta/Erupe/server/launcherserver" + "github.com/Andoryuuta/Erupe/server/signserver" _ "github.com/lib/pq" "go.uber.org/zap" ) diff --git a/channelserver/channel_server.go b/server/channelserver/channel_server.go similarity index 100% rename from channelserver/channel_server.go rename to server/channelserver/channel_server.go diff --git a/channelserver/handler_table.go b/server/channelserver/handler_table.go similarity index 100% rename from channelserver/handler_table.go rename to server/channelserver/handler_table.go diff --git a/channelserver/handlers.go b/server/channelserver/handlers.go similarity index 100% rename from channelserver/handlers.go rename to server/channelserver/handlers.go diff --git a/channelserver/session.go b/server/channelserver/session.go similarity index 100% rename from channelserver/session.go rename to server/channelserver/session.go diff --git a/entranceserver/crypto.go b/server/entranceserver/crypto.go similarity index 100% rename from entranceserver/crypto.go rename to server/entranceserver/crypto.go diff --git a/entranceserver/crypto_test.go b/server/entranceserver/crypto_test.go similarity index 100% rename from entranceserver/crypto_test.go rename to server/entranceserver/crypto_test.go diff --git a/entranceserver/entrance_server.go b/server/entranceserver/entrance_server.go similarity index 100% rename from entranceserver/entrance_server.go rename to server/entranceserver/entrance_server.go diff --git a/entranceserver/make_resp.go b/server/entranceserver/make_resp.go similarity index 100% rename from entranceserver/make_resp.go rename to server/entranceserver/make_resp.go diff --git a/launcherserver/handler.go b/server/launcherserver/handler.go similarity index 100% rename from launcherserver/handler.go rename to server/launcherserver/handler.go diff --git a/launcherserver/launcher_server.go b/server/launcherserver/launcher_server.go similarity index 100% rename from launcherserver/launcher_server.go rename to server/launcherserver/launcher_server.go diff --git a/launcherserver/routes.go b/server/launcherserver/routes.go similarity index 100% rename from launcherserver/routes.go rename to server/launcherserver/routes.go diff --git a/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go similarity index 100% rename from signserver/dsgn_resp.go rename to server/signserver/dsgn_resp.go diff --git a/signserver/respid.go b/server/signserver/respid.go similarity index 100% rename from signserver/respid.go rename to server/signserver/respid.go diff --git a/signserver/session.go b/server/signserver/session.go similarity index 100% rename from signserver/session.go rename to server/signserver/session.go diff --git a/signserver/sign_server.go b/server/signserver/sign_server.go similarity index 100% rename from signserver/sign_server.go rename to server/signserver/sign_server.go From 5f1d429c1280e4cfd11a436e55e86c7db79c3ea9 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Mon, 13 Jan 2020 18:36:55 -0500 Subject: [PATCH 03/49] Add graceful shutdown to channel server --- main.go | 4 +++ server/channelserver/channel_server.go | 37 +++++++++++++++++++++++--- server/channelserver/session.go | 9 ++++--- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/main.go b/main.go index f197959bf..3490f8ea4 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "os" "os/signal" "syscall" + "time" "github.com/Andoryuuta/Erupe/config" "github.com/Andoryuuta/Erupe/server/channelserver" @@ -113,7 +114,10 @@ func main() { <-c logger.Info("Trying to shutdown gracefully.") + channelServer.Shutdown() signServer.Shutdown() entranceServer.Shutdown() launcherServer.Shutdown() + + time.Sleep(5 * time.Second) } diff --git a/server/channelserver/channel_server.go b/server/channelserver/channel_server.go index e70d922fc..5c43e7e55 100644 --- a/server/channelserver/channel_server.go +++ b/server/channelserver/channel_server.go @@ -26,8 +26,9 @@ type Server struct { acceptConns chan net.Conn deleteConns chan net.Conn sessions map[net.Conn]*Session - listenAddr string listener net.Listener // Listener that is created when Server.Start is called. + + isShuttingDown bool } // NewServer creates a new Server type. @@ -57,13 +58,30 @@ func (s *Server) Start() error { return nil } +// Shutdown tries to shut down the server gracefully. +func (s *Server) Shutdown() { + s.Lock() + s.isShuttingDown = true + s.Unlock() + + s.listener.Close() + close(s.acceptConns) +} + func (s *Server) acceptClients() { for { conn, err := s.listener.Accept() if err != nil { - // TODO(Andoryuuta): Implement shutdown logic to end this goroutine cleanly here. - fmt.Println(err) - continue + s.Lock() + shutdown := s.isShuttingDown + s.Unlock() + + if shutdown { + break + } else { + s.logger.Warn("Error accepting client", zap.Error(err)) + continue + } } s.acceptConns <- conn } @@ -73,6 +91,17 @@ func (s *Server) manageSessions() { for { select { case newConn := <-s.acceptConns: + // Gracefully handle acceptConns channel closing. + if newConn == nil { + s.Lock() + shutdown := s.isShuttingDown + s.Unlock() + + if shutdown { + return + } + } + session := NewSession(s, newConn) s.Lock() diff --git a/server/channelserver/session.go b/server/channelserver/session.go index 0e95d1b14..0e0a0e079 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -10,11 +10,13 @@ import ( "github.com/Andoryuuta/Erupe/network" "github.com/Andoryuuta/Erupe/network/mhfpacket" "github.com/Andoryuuta/byteframe" + "go.uber.org/zap" ) // Session holds state for the channel server connection. type Session struct { sync.Mutex + logger *zap.Logger server *Server rawConn net.Conn cryptConn *network.CryptConn @@ -23,6 +25,7 @@ type Session struct { // NewSession creates a new Session type. func NewSession(server *Server, conn net.Conn) *Session { s := &Session{ + logger: server.logger, server: server, rawConn: conn, cryptConn: network.NewCryptConn(conn), @@ -33,15 +36,15 @@ func NewSession(server *Server, conn net.Conn) *Session { // Start starts the session packet read&handle loop. func (s *Session) Start() { go func() { - fmt.Println("Channel server got connection!") + s.logger.Info("Channel server got connection!") + // Unlike the sign and entrance server, // the client DOES NOT initalize the channel connection with 8 NULL bytes. for { pkt, err := s.cryptConn.ReadPacket() if err != nil { - fmt.Println(err) - fmt.Println("Error on channel server readpacket") + s.logger.Warn("Error on channel server readpacket", zap.Error(err)) return } From e6d7b7b9c2b717382ea1c409eb95fe5841db3a19 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Tue, 14 Jan 2020 11:23:27 -0500 Subject: [PATCH 04/49] Fix sum32 checksum --- server/entranceserver/crypto.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/server/entranceserver/crypto.go b/server/entranceserver/crypto.go index 7fec6b7c5..20a361e7e 100644 --- a/server/entranceserver/crypto.go +++ b/server/entranceserver/crypto.go @@ -6,23 +6,22 @@ import ( var ( _bin8Key = []byte{0x01, 0x23, 0x34, 0x45, 0x56, 0xAB, 0xCD, 0xEF} - _sum32Table0 = []byte{0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12, 0xDE, 0xDE, 0x35} - _sum32Table1 = []byte{0x35, 0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12} + _sum32Table0 = []byte{0x35, 0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12} + _sum32Table1 = []byte{0x7A, 0xAA, 0x97, 0x53, 0x66, 0x12, 0xDE, 0xDE, 0x35} ) // CalcSum32 calculates the custom MHF "sum32" checksum of the given data. func CalcSum32(data []byte) uint32 { - tableIdx0 := byte(len(data) & 0xFF) - tableIdx1 := byte(data[len(data)>>1]) + tableIdx0 := int(len(data) & 0xFF) + tableIdx1 := int(data[len(data)>>1] & 0xFF) out := make([]byte, 4) for i := 0; i < len(data); i++ { tableIdx0++ tableIdx1++ - tmp := byte((_sum32Table0[tableIdx1%9] ^ _sum32Table1[tableIdx0%7]) ^ data[i]) + tmp := byte((_sum32Table1[tableIdx1%9] ^ _sum32Table0[tableIdx0%7]) ^ data[i]) out[i&3] = (out[i&3] + tmp) & 0xFF - } return binary.BigEndian.Uint32(out) From fa608fa5552130c9108c6b65c6021cab265a7eec Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Sun, 19 Jan 2020 12:52:57 -0500 Subject: [PATCH 05/49] Initial chat prototype --- .gitignore | 4 +- config.json | 34 +- go.mod | 4 +- go.sum | 7 + main.go | 19 +- migrations/000002_alter_characters.down.sql | 8 + migrations/000002_alter_characters.up.sql | 8 + network/mhfpacket/msg_mhf_enumerate_order.go | 13 +- network/mhfpacket/msg_mhf_enumerate_price.go | 13 +- network/mhfpacket/msg_mhf_enumerate_shop.go | 21 +- network/mhfpacket/msg_mhf_get_boost_right.go | 9 +- network/mhfpacket/msg_mhf_get_boost_time.go | 9 +- .../mhfpacket/msg_mhf_get_boost_time_limit.go | 9 +- network/mhfpacket/msg_mhf_get_earth_status.go | 13 +- network/mhfpacket/msg_mhf_get_earth_value.go | 23 +- .../msg_mhf_get_enhanced_minidata.go | 11 +- .../msg_mhf_get_keep_login_boost_status.go | 9 +- .../mhfpacket/msg_mhf_get_kouryou_point.go | 9 +- network/mhfpacket/msg_mhf_get_paper_data.go | 15 +- network/mhfpacket/msg_mhf_get_tower_info.go | 25 +- network/mhfpacket/msg_mhf_get_ud_info.go | 9 +- .../mhfpacket/msg_mhf_get_ud_monster_point.go | 9 +- network/mhfpacket/msg_mhf_get_ud_schedule.go | 9 +- .../mhfpacket/msg_mhf_get_weekly_schedule.go | 9 +- network/mhfpacket/msg_mhf_info_festa.go | 13 +- network/mhfpacket/msg_mhf_load_deco_myset.go | 9 +- .../mhfpacket/msg_mhf_load_favorite_quest.go | 9 +- network/mhfpacket/msg_mhf_load_hunter_navi.go | 9 +- network/mhfpacket/msg_mhf_load_mezfes_data.go | 9 +- network/mhfpacket/msg_mhf_load_otomo_airou.go | 7 +- network/mhfpacket/msg_mhf_load_partner.go | 7 +- network/mhfpacket/msg_mhf_load_plate_box.go | 9 +- network/mhfpacket/msg_mhf_load_plate_data.go | 9 +- network/mhfpacket/msg_mhf_load_plate_myset.go | 9 +- .../mhfpacket/msg_mhf_load_rengoku_data.go | 9 +- .../mhfpacket/msg_mhf_load_scenario_data.go | 9 +- network/mhfpacket/msg_mhf_read_beat_level.go | 20 +- network/mhfpacket/msg_mhf_read_mercenary_w.go | 2 +- network/mhfpacket/msg_mhf_save_hunter_navi.go | 15 +- network/mhfpacket/msg_mhf_save_mezfes_data.go | 13 +- network/mhfpacket/msg_mhf_save_partner.go | 13 +- .../mhfpacket/msg_mhf_save_scenario_data.go | 13 +- network/mhfpacket/msg_mhf_savedata.go | 19 +- .../msg_mhf_set_enhanced_minidata.go | 13 +- network/mhfpacket/msg_sys_ack.go | 11 +- network/mhfpacket/msg_sys_cast_binary.go | 18 +- network/mhfpacket/msg_sys_casted_binary.go | 16 +- network/mhfpacket/msg_sys_create_object.go | 15 +- network/mhfpacket/msg_sys_end.go | 2 + network/mhfpacket/msg_sys_enter_stage.go | 15 +- network/mhfpacket/msg_sys_extend_threshold.go | 2 + network/mhfpacket/msg_sys_hide_client.go | 11 +- network/mhfpacket/msg_sys_nop.go | 2 + network/mhfpacket/msg_sys_ping.go | 3 - network/mhfpacket/msg_sys_position_object.go | 11 +- network/mhfpacket/msg_sys_reserve188.go | 9 +- network/mhfpacket/msg_sys_reserve18b.go | 9 +- .../mhfpacket/msg_sys_set_object_binary.go | 13 +- network/mhfpacket/msg_sys_set_user_binary.go | 13 +- network/mhfpacket/msg_sys_terminal_log.go | 37 +- network/mhfpacket/msg_sys_time.go | 8 +- network/mhfpacket/msg_sys_update_right.go | 49 +- server/channelserver/channel_server.go | 30 +- server/channelserver/handlers.go | 886 ++++++++++++++++-- server/channelserver/session.go | 475 ++-------- server/entranceserver/entrance_server.go | 6 +- server/entranceserver/make_resp.go | 10 + server/launcherserver/launcher_server.go | 6 +- server/launcherserver/routes.go | 2 +- server/signserver/dbutils.go | 56 ++ server/signserver/dsgn_resp.go | 103 +- server/signserver/session.go | 21 +- server/signserver/sign_server.go | 6 +- 73 files changed, 1646 insertions(+), 714 deletions(-) create mode 100644 migrations/000002_alter_characters.down.sql create mode 100644 migrations/000002_alter_characters.up.sql create mode 100644 server/signserver/dbutils.go diff --git a/.gitignore b/.gitignore index 8d6aeaab4..be42fbcb3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,10 @@ www/tw/ www/jp/ bin_resp/ +savedata/ custom_entrance_server_resp.bin dec_bin8_data_dump.bin entrance_resp_bin8_encrypted.bin tw_server_list_resp.bin -Erupe.exe \ No newline at end of file +Erupe.exe +test.py \ No newline at end of file diff --git a/config.json b/config.json index cfb58cd45..6dafbae4c 100644 --- a/config.json +++ b/config.json @@ -21,17 +21,17 @@ "port": 53310, "entries": [ { - "name": "AErupe server 1", + "name": "AErupe server noob", "ip": "127.0.0.1", "unk2": 0, - "type": 1, + "type": 3, "season": 0, - "unk6": 3, + "unk6": 0, "allowedclientflags": "4096", "channels": [ { "port": 54001, - "MaxPlayers": 100, + "MaxPlayers": 10, "CurrentPlayers": 0, "Unk4": 0, "Unk5": 0, @@ -45,6 +45,32 @@ "Unk13": 12345 } ] + }, + { + "name": "AErupe server open", + "ip": "127.0.0.1", + "unk2": 0, + "type": 1, + "season": 0, + "unk6": 0, + "allowedclientflags": 0, + "channels": [ + { + "port": 54001, + "MaxPlayers": 10, + "CurrentPlayers": 0, + "Unk4": 0, + "Unk5": 0, + "Unk6": 0, + "Unk7": 0, + "Unk8": 0, + "Unk9": 0, + "Unk10": 318, + "Unk11": 251, + "Unk12": 155, + "Unk13": 12345 + } + ] } ] } diff --git a/go.mod b/go.mod index 7f512f3a0..a2635d524 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,17 @@ module github.com/Andoryuuta/Erupe go 1.13 require ( - github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0 + github.com/Andoryuuta/byteframe v0.0.0-20200114030334-8979c5cc4c4a github.com/BurntSushi/toml v0.3.1 github.com/gorilla/handlers v1.4.2 github.com/gorilla/mux v1.7.3 + github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.3.0 github.com/spf13/viper v1.6.1 go.uber.org/atomic v1.5.1 // indirect go.uber.org/multierr v1.4.0 // indirect go.uber.org/zap v1.13.0 golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f // indirect + golang.org/x/text v0.3.0 golang.org/x/tools v0.0.0-20200110213125-a7a6caa82ab2 // indirect ) diff --git a/go.sum b/go.sum index b236b54e0..575a8db8b 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0 h1:2pVgen9rh18IxSWxOa80bObcpyfrS6d5bJtZeCUN7rY= github.com/Andoryuuta/byteframe v0.0.0-20191219124302-41f4085eb4c0/go.mod h1:koVyx+gN3TfE70rpOidywETVODk87304YpwW69Y27J4= +github.com/Andoryuuta/byteframe v0.0.0-20200114030334-8979c5cc4c4a h1:41dzqxDfdVhYjpkr8lxwrBdJe0RE/AXsGV1AGpP6wig= +github.com/Andoryuuta/byteframe v0.0.0-20200114030334-8979c5cc4c4a/go.mod h1:koVyx+gN3TfE70rpOidywETVODk87304YpwW69Y27J4= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -26,6 +28,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= @@ -48,6 +51,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -58,10 +63,12 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= diff --git a/main.go b/main.go index 3490f8ea4..296dd248e 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - "database/sql" "fmt" "os" "os/signal" @@ -13,10 +12,18 @@ import ( "github.com/Andoryuuta/Erupe/server/entranceserver" "github.com/Andoryuuta/Erupe/server/launcherserver" "github.com/Andoryuuta/Erupe/server/signserver" + "github.com/jmoiron/sqlx" _ "github.com/lib/pq" "go.uber.org/zap" ) +// Temporary DB auto clean on startup for quick development & testing. +func cleanDB(db *sqlx.DB) { + _ = db.MustExec("DELETE FROM characters") + _ = db.MustExec("DELETE FROM sign_sessions") + _ = db.MustExec("DELETE FROM users") +} + func main() { zapLogger, _ := zap.NewDevelopment() defer zapLogger.Sync() @@ -40,7 +47,7 @@ func main() { erupeConfig.Database.Database, ) - db, err := sql.Open("postgres", connectString) + db, err := sqlx.Open("postgres", connectString) if err != nil { logger.Fatal("Failed to open sql database", zap.Error(err)) } @@ -50,7 +57,11 @@ func main() { if err != nil { logger.Fatal("Failed to ping database", zap.Error(err)) } - logger.Info("Connected to database.") + logger.Info("Connected to database") + + logger.Info("Cleaning DB") + cleanDB(db) + logger.Info("Done cleaning DB") // Now start our server(s). @@ -119,5 +130,5 @@ func main() { entranceServer.Shutdown() launcherServer.Shutdown() - time.Sleep(5 * time.Second) + time.Sleep(1 * time.Second) } diff --git a/migrations/000002_alter_characters.down.sql b/migrations/000002_alter_characters.down.sql new file mode 100644 index 000000000..c5eeb425b --- /dev/null +++ b/migrations/000002_alter_characters.down.sql @@ -0,0 +1,8 @@ +BEGIN; + +ALTER TABLE characters + DROP COLUMN exp, + DROP COLUMN weapon, + DROP COLUMN last_login; + +END; \ No newline at end of file diff --git a/migrations/000002_alter_characters.up.sql b/migrations/000002_alter_characters.up.sql new file mode 100644 index 000000000..8e92154dc --- /dev/null +++ b/migrations/000002_alter_characters.up.sql @@ -0,0 +1,8 @@ +BEGIN; + +ALTER TABLE characters + ADD COLUMN exp uint16, + ADD COLUMN weapon uint16, + ADD COLUMN last_login integer; + +END; \ No newline at end of file diff --git a/network/mhfpacket/msg_mhf_enumerate_order.go b/network/mhfpacket/msg_mhf_enumerate_order.go index 5c3dc0a93..a5c26eb9a 100644 --- a/network/mhfpacket/msg_mhf_enumerate_order.go +++ b/network/mhfpacket/msg_mhf_enumerate_order.go @@ -6,7 +6,11 @@ import ( ) // MsgMhfEnumerateOrder represents the MSG_MHF_ENUMERATE_ORDER -type MsgMhfEnumerateOrder struct{} +type MsgMhfEnumerateOrder struct { + AckHandle uint32 + Unk0 uint32 + Unk1 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfEnumerateOrder) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgMhfEnumerateOrder) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateOrder) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.Unk1 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfEnumerateOrder) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_enumerate_price.go b/network/mhfpacket/msg_mhf_enumerate_price.go index ade7862c8..1e5b5fa6d 100644 --- a/network/mhfpacket/msg_mhf_enumerate_price.go +++ b/network/mhfpacket/msg_mhf_enumerate_price.go @@ -6,7 +6,11 @@ import ( ) // MsgMhfEnumeratePrice represents the MSG_MHF_ENUMERATE_PRICE -type MsgMhfEnumeratePrice struct{} +type MsgMhfEnumeratePrice struct { + AckHandle uint32 + Unk0 uint16 // Hardcoded 0 in the binary + Unk1 uint16 // Hardcoded 0 in the binary +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfEnumeratePrice) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgMhfEnumeratePrice) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumeratePrice) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfEnumeratePrice) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_enumerate_shop.go b/network/mhfpacket/msg_mhf_enumerate_shop.go index 938393c44..da3ad308f 100644 --- a/network/mhfpacket/msg_mhf_enumerate_shop.go +++ b/network/mhfpacket/msg_mhf_enumerate_shop.go @@ -6,7 +6,15 @@ import ( ) // MsgMhfEnumerateShop represents the MSG_MHF_ENUMERATE_SHOP -type MsgMhfEnumerateShop struct{} +type MsgMhfEnumerateShop struct { + AckHandle uint32 + Unk0 uint8 // Shop ID maybe? I seen 0 -> 10. + Unk1 uint32 + Unk2 uint16 + Unk3 uint8 + Unk4 uint8 + Unk5 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfEnumerateShop) Opcode() network.PacketID { @@ -15,10 +23,17 @@ func (m *MsgMhfEnumerateShop) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateShop) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint32() + m.Unk2 = bf.ReadUint16() + m.Unk3 = bf.ReadUint8() + m.Unk4 = bf.ReadUint8() + m.Unk5 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfEnumerateShop) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_boost_right.go b/network/mhfpacket/msg_mhf_get_boost_right.go index d520aab3d..4b48c2203 100644 --- a/network/mhfpacket/msg_mhf_get_boost_right.go +++ b/network/mhfpacket/msg_mhf_get_boost_right.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetBoostRight represents the MSG_MHF_GET_BOOST_RIGHT -type MsgMhfGetBoostRight struct{} +type MsgMhfGetBoostRight struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetBoostRight) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfGetBoostRight) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetBoostRight) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetBoostRight) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_boost_time.go b/network/mhfpacket/msg_mhf_get_boost_time.go index e0bea7a18..12da72f23 100644 --- a/network/mhfpacket/msg_mhf_get_boost_time.go +++ b/network/mhfpacket/msg_mhf_get_boost_time.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetBoostTime represents the MSG_MHF_GET_BOOST_TIME -type MsgMhfGetBoostTime struct{} +type MsgMhfGetBoostTime struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetBoostTime) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfGetBoostTime) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetBoostTime) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetBoostTime) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_boost_time_limit.go b/network/mhfpacket/msg_mhf_get_boost_time_limit.go index d2ad3a758..0c234e12f 100644 --- a/network/mhfpacket/msg_mhf_get_boost_time_limit.go +++ b/network/mhfpacket/msg_mhf_get_boost_time_limit.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetBoostTimeLimit represents the MSG_MHF_GET_BOOST_TIME_LIMIT -type MsgMhfGetBoostTimeLimit struct{} +type MsgMhfGetBoostTimeLimit struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetBoostTimeLimit) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfGetBoostTimeLimit) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetBoostTimeLimit) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetBoostTimeLimit) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_earth_status.go b/network/mhfpacket/msg_mhf_get_earth_status.go index f1e699d25..0ba575040 100644 --- a/network/mhfpacket/msg_mhf_get_earth_status.go +++ b/network/mhfpacket/msg_mhf_get_earth_status.go @@ -6,7 +6,11 @@ import ( ) // MsgMhfGetEarthStatus represents the MSG_MHF_GET_EARTH_STATUS -type MsgMhfGetEarthStatus struct{} +type MsgMhfGetEarthStatus struct { + AckHandle uint32 + Unk0 uint32 + Unk1 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetEarthStatus) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgMhfGetEarthStatus) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetEarthStatus) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.Unk1 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetEarthStatus) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_earth_value.go b/network/mhfpacket/msg_mhf_get_earth_value.go index da435d462..de83c0a61 100644 --- a/network/mhfpacket/msg_mhf_get_earth_value.go +++ b/network/mhfpacket/msg_mhf_get_earth_value.go @@ -6,7 +6,16 @@ import ( ) // MsgMhfGetEarthValue represents the MSG_MHF_GET_EARTH_VALUE -type MsgMhfGetEarthValue struct{} +type MsgMhfGetEarthValue struct { + AckHandle uint32 + Unk0 uint32 + Unk1 uint32 + Unk2 uint32 + Unk3 uint32 + Unk4 uint32 + Unk5 uint32 + Unk6 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetEarthValue) Opcode() network.PacketID { @@ -15,10 +24,18 @@ func (m *MsgMhfGetEarthValue) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetEarthValue) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.Unk1 = bf.ReadUint32() + m.Unk2 = bf.ReadUint32() + m.Unk3 = bf.ReadUint32() + m.Unk4 = bf.ReadUint32() + m.Unk5 = bf.ReadUint32() + m.Unk6 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetEarthValue) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_enhanced_minidata.go b/network/mhfpacket/msg_mhf_get_enhanced_minidata.go index cf2d56592..ff03df97a 100644 --- a/network/mhfpacket/msg_mhf_get_enhanced_minidata.go +++ b/network/mhfpacket/msg_mhf_get_enhanced_minidata.go @@ -6,7 +6,10 @@ import ( ) // MsgMhfGetEnhancedMinidata represents the MSG_MHF_GET_ENHANCED_MINIDATA -type MsgMhfGetEnhancedMinidata struct{} +type MsgMhfGetEnhancedMinidata struct { + AckHandle uint32 + CharID uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetEnhancedMinidata) Opcode() network.PacketID { @@ -15,10 +18,12 @@ func (m *MsgMhfGetEnhancedMinidata) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetEnhancedMinidata) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.CharID = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetEnhancedMinidata) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_keep_login_boost_status.go b/network/mhfpacket/msg_mhf_get_keep_login_boost_status.go index 9ca1ed5a1..43022755a 100644 --- a/network/mhfpacket/msg_mhf_get_keep_login_boost_status.go +++ b/network/mhfpacket/msg_mhf_get_keep_login_boost_status.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetKeepLoginBoostStatus represents the MSG_MHF_GET_KEEP_LOGIN_BOOST_STATUS -type MsgMhfGetKeepLoginBoostStatus struct{} +type MsgMhfGetKeepLoginBoostStatus struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetKeepLoginBoostStatus) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfGetKeepLoginBoostStatus) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetKeepLoginBoostStatus) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetKeepLoginBoostStatus) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_kouryou_point.go b/network/mhfpacket/msg_mhf_get_kouryou_point.go index 434c07876..e279509da 100644 --- a/network/mhfpacket/msg_mhf_get_kouryou_point.go +++ b/network/mhfpacket/msg_mhf_get_kouryou_point.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetKouryouPoint represents the MSG_MHF_GET_KOURYOU_POINT -type MsgMhfGetKouryouPoint struct{} +type MsgMhfGetKouryouPoint struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetKouryouPoint) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfGetKouryouPoint) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetKouryouPoint) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetKouryouPoint) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_paper_data.go b/network/mhfpacket/msg_mhf_get_paper_data.go index 7782f6f0f..19d5207f6 100644 --- a/network/mhfpacket/msg_mhf_get_paper_data.go +++ b/network/mhfpacket/msg_mhf_get_paper_data.go @@ -6,7 +6,12 @@ import ( ) // MsgMhfGetPaperData represents the MSG_MHF_GET_PAPER_DATA -type MsgMhfGetPaperData struct{} +type MsgMhfGetPaperData struct { + AckHandle uint32 + Unk0 uint32 + Unk1 uint32 + Unk2 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetPaperData) Opcode() network.PacketID { @@ -15,10 +20,14 @@ func (m *MsgMhfGetPaperData) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetPaperData) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.Unk1 = bf.ReadUint32() + m.Unk2 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetPaperData) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_tower_info.go b/network/mhfpacket/msg_mhf_get_tower_info.go index c2bbcdf62..5cb86cbc7 100644 --- a/network/mhfpacket/msg_mhf_get_tower_info.go +++ b/network/mhfpacket/msg_mhf_get_tower_info.go @@ -5,8 +5,23 @@ import ( "github.com/Andoryuuta/byteframe" ) +// The server sends different responses based on these values. +const ( + TowerInfoTypeUnk0 = iota + TowerInfoTypeTowerRankPoint + TowerInfoTypeGetOwnTowerSkill + TowerInfoTypeUnk3 + TowerInfoTypeTowerTouhaHistory + TowerInfoTypeUnk5 +) + // MsgMhfGetTowerInfo represents the MSG_MHF_GET_TOWER_INFO -type MsgMhfGetTowerInfo struct{} +type MsgMhfGetTowerInfo struct { + AckHandle uint32 + InfoType uint32 // Requested response type + Unk0 uint32 + Unk1 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetTowerInfo) Opcode() network.PacketID { @@ -15,10 +30,14 @@ func (m *MsgMhfGetTowerInfo) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetTowerInfo) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.InfoType = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.Unk1 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetTowerInfo) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_ud_info.go b/network/mhfpacket/msg_mhf_get_ud_info.go index d73402c97..0647fb198 100644 --- a/network/mhfpacket/msg_mhf_get_ud_info.go +++ b/network/mhfpacket/msg_mhf_get_ud_info.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetUdInfo represents the MSG_MHF_GET_UD_INFO -type MsgMhfGetUdInfo struct{} +type MsgMhfGetUdInfo struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetUdInfo) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfGetUdInfo) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetUdInfo) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetUdInfo) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_ud_monster_point.go b/network/mhfpacket/msg_mhf_get_ud_monster_point.go index 01de9256b..1311541ed 100644 --- a/network/mhfpacket/msg_mhf_get_ud_monster_point.go +++ b/network/mhfpacket/msg_mhf_get_ud_monster_point.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetUdMonsterPoint represents the MSG_MHF_GET_UD_MONSTER_POINT -type MsgMhfGetUdMonsterPoint struct{} +type MsgMhfGetUdMonsterPoint struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetUdMonsterPoint) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfGetUdMonsterPoint) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetUdMonsterPoint) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetUdMonsterPoint) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_ud_schedule.go b/network/mhfpacket/msg_mhf_get_ud_schedule.go index bbe23ea56..f543cce0c 100644 --- a/network/mhfpacket/msg_mhf_get_ud_schedule.go +++ b/network/mhfpacket/msg_mhf_get_ud_schedule.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetUdSchedule represents the MSG_MHF_GET_UD_SCHEDULE -type MsgMhfGetUdSchedule struct{} +type MsgMhfGetUdSchedule struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetUdSchedule) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfGetUdSchedule) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetUdSchedule) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetUdSchedule) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_weekly_schedule.go b/network/mhfpacket/msg_mhf_get_weekly_schedule.go index b6458028b..c8a01cb7b 100644 --- a/network/mhfpacket/msg_mhf_get_weekly_schedule.go +++ b/network/mhfpacket/msg_mhf_get_weekly_schedule.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetWeeklySchedule represents the MSG_MHF_GET_WEEKLY_SCHEDULE -type MsgMhfGetWeeklySchedule struct{} +type MsgMhfGetWeeklySchedule struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetWeeklySchedule) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfGetWeeklySchedule) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetWeeklySchedule) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetWeeklySchedule) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_info_festa.go b/network/mhfpacket/msg_mhf_info_festa.go index 0e65fac3d..f376f77ff 100644 --- a/network/mhfpacket/msg_mhf_info_festa.go +++ b/network/mhfpacket/msg_mhf_info_festa.go @@ -6,7 +6,11 @@ import ( ) // MsgMhfInfoFesta represents the MSG_MHF_INFO_FESTA -type MsgMhfInfoFesta struct{} +type MsgMhfInfoFesta struct { + AckHandle uint32 + Unk0 uint16 // Hardcoded 0 in the binary + Unk1 uint16 // Hardcoded 0 in the binary +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfInfoFesta) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgMhfInfoFesta) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfInfoFesta) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfInfoFesta) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_load_deco_myset.go b/network/mhfpacket/msg_mhf_load_deco_myset.go index 9b0040f9d..e2f97f326 100644 --- a/network/mhfpacket/msg_mhf_load_deco_myset.go +++ b/network/mhfpacket/msg_mhf_load_deco_myset.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoadDecoMyset represents the MSG_MHF_LOAD_DECO_MYSET -type MsgMhfLoadDecoMyset struct{} +type MsgMhfLoadDecoMyset struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoadDecoMyset) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfLoadDecoMyset) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadDecoMyset) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfLoadDecoMyset) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_load_favorite_quest.go b/network/mhfpacket/msg_mhf_load_favorite_quest.go index b1da3c9f5..f3224962a 100644 --- a/network/mhfpacket/msg_mhf_load_favorite_quest.go +++ b/network/mhfpacket/msg_mhf_load_favorite_quest.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoadFavoriteQuest represents the MSG_MHF_LOAD_FAVORITE_QUEST -type MsgMhfLoadFavoriteQuest struct{} +type MsgMhfLoadFavoriteQuest struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoadFavoriteQuest) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfLoadFavoriteQuest) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadFavoriteQuest) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfLoadFavoriteQuest) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_load_hunter_navi.go b/network/mhfpacket/msg_mhf_load_hunter_navi.go index fb30a0562..777e983a7 100644 --- a/network/mhfpacket/msg_mhf_load_hunter_navi.go +++ b/network/mhfpacket/msg_mhf_load_hunter_navi.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoadHunterNavi represents the MSG_MHF_LOAD_HUNTER_NAVI -type MsgMhfLoadHunterNavi struct{} +type MsgMhfLoadHunterNavi struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoadHunterNavi) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfLoadHunterNavi) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadHunterNavi) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfLoadHunterNavi) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_load_mezfes_data.go b/network/mhfpacket/msg_mhf_load_mezfes_data.go index 1e265f3a6..4f31a61e9 100644 --- a/network/mhfpacket/msg_mhf_load_mezfes_data.go +++ b/network/mhfpacket/msg_mhf_load_mezfes_data.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoadMezfesData represents the MSG_MHF_LOAD_MEZFES_DATA -type MsgMhfLoadMezfesData struct{} +type MsgMhfLoadMezfesData struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoadMezfesData) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfLoadMezfesData) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadMezfesData) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfLoadMezfesData) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_load_otomo_airou.go b/network/mhfpacket/msg_mhf_load_otomo_airou.go index bf3bbdbe8..33b7dc92c 100644 --- a/network/mhfpacket/msg_mhf_load_otomo_airou.go +++ b/network/mhfpacket/msg_mhf_load_otomo_airou.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoadOtomoAirou represents the MSG_MHF_LOAD_OTOMO_AIROU -type MsgMhfLoadOtomoAirou struct{} +type MsgMhfLoadOtomoAirou struct{ + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoadOtomoAirou) Opcode() network.PacketID { @@ -15,7 +17,8 @@ func (m *MsgMhfLoadOtomoAirou) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadOtomoAirou) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_load_partner.go b/network/mhfpacket/msg_mhf_load_partner.go index 63b0e9a53..703308a2e 100644 --- a/network/mhfpacket/msg_mhf_load_partner.go +++ b/network/mhfpacket/msg_mhf_load_partner.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoadPartner represents the MSG_MHF_LOAD_PARTNER -type MsgMhfLoadPartner struct{} +type MsgMhfLoadPartner struct{ + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoadPartner) Opcode() network.PacketID { @@ -15,7 +17,8 @@ func (m *MsgMhfLoadPartner) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadPartner) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_load_plate_box.go b/network/mhfpacket/msg_mhf_load_plate_box.go index 8625e2038..b6d2e2ca2 100644 --- a/network/mhfpacket/msg_mhf_load_plate_box.go +++ b/network/mhfpacket/msg_mhf_load_plate_box.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoadPlateBox represents the MSG_MHF_LOAD_PLATE_BOX -type MsgMhfLoadPlateBox struct{} +type MsgMhfLoadPlateBox struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoadPlateBox) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfLoadPlateBox) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadPlateBox) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfLoadPlateBox) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_load_plate_data.go b/network/mhfpacket/msg_mhf_load_plate_data.go index 5fc835c06..f7b16339c 100644 --- a/network/mhfpacket/msg_mhf_load_plate_data.go +++ b/network/mhfpacket/msg_mhf_load_plate_data.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoadPlateData represents the MSG_MHF_LOAD_PLATE_DATA -type MsgMhfLoadPlateData struct{} +type MsgMhfLoadPlateData struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoadPlateData) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfLoadPlateData) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadPlateData) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfLoadPlateData) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_load_plate_myset.go b/network/mhfpacket/msg_mhf_load_plate_myset.go index 831d807ce..429f62c37 100644 --- a/network/mhfpacket/msg_mhf_load_plate_myset.go +++ b/network/mhfpacket/msg_mhf_load_plate_myset.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoadPlateMyset represents the MSG_MHF_LOAD_PLATE_MYSET -type MsgMhfLoadPlateMyset struct{} +type MsgMhfLoadPlateMyset struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoadPlateMyset) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfLoadPlateMyset) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadPlateMyset) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfLoadPlateMyset) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_load_rengoku_data.go b/network/mhfpacket/msg_mhf_load_rengoku_data.go index 9005716a8..98c9396cf 100644 --- a/network/mhfpacket/msg_mhf_load_rengoku_data.go +++ b/network/mhfpacket/msg_mhf_load_rengoku_data.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoadRengokuData represents the MSG_MHF_LOAD_RENGOKU_DATA -type MsgMhfLoadRengokuData struct{} +type MsgMhfLoadRengokuData struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoadRengokuData) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfLoadRengokuData) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadRengokuData) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfLoadRengokuData) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_load_scenario_data.go b/network/mhfpacket/msg_mhf_load_scenario_data.go index b115c0712..911a555c0 100644 --- a/network/mhfpacket/msg_mhf_load_scenario_data.go +++ b/network/mhfpacket/msg_mhf_load_scenario_data.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoadScenarioData represents the MSG_MHF_LOAD_SCENARIO_DATA -type MsgMhfLoadScenarioData struct{} +type MsgMhfLoadScenarioData struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoadScenarioData) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfLoadScenarioData) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoadScenarioData) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfLoadScenarioData) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_read_beat_level.go b/network/mhfpacket/msg_mhf_read_beat_level.go index 85a6cd334..2110c580c 100644 --- a/network/mhfpacket/msg_mhf_read_beat_level.go +++ b/network/mhfpacket/msg_mhf_read_beat_level.go @@ -6,7 +6,12 @@ import ( ) // MsgMhfReadBeatLevel represents the MSG_MHF_READ_BEAT_LEVEL -type MsgMhfReadBeatLevel struct{} +type MsgMhfReadBeatLevel struct { + AckHandle uint32 + Unk0 uint32 + ValidIDCount uint32 // Valid entries in the array + IDs [16]uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfReadBeatLevel) Opcode() network.PacketID { @@ -15,10 +20,19 @@ func (m *MsgMhfReadBeatLevel) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfReadBeatLevel) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + // I assume this used to be dynamic, but as of the last JP client version, all of this data is hard-coded literals. + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() // Always 1 + m.ValidIDCount = bf.ReadUint32() // Always 4 + + // Always 0x74, 0x6B, 0x02, 0x24 followed by 12 zero values. + for i := 0; i < len(m.IDs); i++ { + m.IDs[i] = bf.ReadUint32() + } + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfReadBeatLevel) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_read_mercenary_w.go b/network/mhfpacket/msg_mhf_read_mercenary_w.go index 89aa4a530..cfd4c6719 100644 --- a/network/mhfpacket/msg_mhf_read_mercenary_w.go +++ b/network/mhfpacket/msg_mhf_read_mercenary_w.go @@ -10,7 +10,7 @@ type MsgMhfReadMercenaryW struct { AckHandle uint32 Unk0 uint8 Unk1 uint8 - Unk2 uint16 + Unk2 uint16 // Hardcoded 0 in the binary } // Opcode returns the ID associated with this packet type. diff --git a/network/mhfpacket/msg_mhf_save_hunter_navi.go b/network/mhfpacket/msg_mhf_save_hunter_navi.go index d62f39254..36b1bdbc1 100644 --- a/network/mhfpacket/msg_mhf_save_hunter_navi.go +++ b/network/mhfpacket/msg_mhf_save_hunter_navi.go @@ -6,7 +6,12 @@ import ( ) // MsgMhfSaveHunterNavi represents the MSG_MHF_SAVE_HUNTER_NAVI -type MsgMhfSaveHunterNavi struct{} +type MsgMhfSaveHunterNavi struct { + AckHandle uint32 + DataSize uint32 + Unk0 bool + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfSaveHunterNavi) Opcode() network.PacketID { @@ -15,10 +20,14 @@ func (m *MsgMhfSaveHunterNavi) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfSaveHunterNavi) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.DataSize = bf.ReadUint32() + m.Unk0 = bf.ReadBool() + m.RawDataPayload = bf.ReadBytes(uint(m.DataSize)) + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfSaveHunterNavi) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_save_mezfes_data.go b/network/mhfpacket/msg_mhf_save_mezfes_data.go index 0c7daa000..96746627f 100644 --- a/network/mhfpacket/msg_mhf_save_mezfes_data.go +++ b/network/mhfpacket/msg_mhf_save_mezfes_data.go @@ -6,7 +6,11 @@ import ( ) // MsgMhfSaveMezfesData represents the MSG_MHF_SAVE_MEZFES_DATA -type MsgMhfSaveMezfesData struct{} +type MsgMhfSaveMezfesData struct { + AckHandle uint32 + DataSize uint32 + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfSaveMezfesData) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgMhfSaveMezfesData) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfSaveMezfesData) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.DataSize = bf.ReadUint32() + m.RawDataPayload = bf.ReadBytes(uint(m.DataSize)) + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfSaveMezfesData) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_save_partner.go b/network/mhfpacket/msg_mhf_save_partner.go index 0bdb84b8c..ee341ab55 100644 --- a/network/mhfpacket/msg_mhf_save_partner.go +++ b/network/mhfpacket/msg_mhf_save_partner.go @@ -6,7 +6,11 @@ import ( ) // MsgMhfSavePartner represents the MSG_MHF_SAVE_PARTNER -type MsgMhfSavePartner struct{} +type MsgMhfSavePartner struct { + AckHandle uint32 + DataSize uint16 + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfSavePartner) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgMhfSavePartner) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfSavePartner) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.DataSize = bf.ReadUint16() + m.RawDataPayload = bf.ReadBytes(uint(m.DataSize)) + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfSavePartner) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_save_scenario_data.go b/network/mhfpacket/msg_mhf_save_scenario_data.go index 6b4ce46a9..7b2d880e0 100644 --- a/network/mhfpacket/msg_mhf_save_scenario_data.go +++ b/network/mhfpacket/msg_mhf_save_scenario_data.go @@ -6,7 +6,11 @@ import ( ) // MsgMhfSaveScenarioData represents the MSG_MHF_SAVE_SCENARIO_DATA -type MsgMhfSaveScenarioData struct{} +type MsgMhfSaveScenarioData struct { + AckHandle uint32 + DataSize uint32 + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfSaveScenarioData) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgMhfSaveScenarioData) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfSaveScenarioData) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.DataSize = bf.ReadUint32() + m.RawDataPayload = bf.ReadBytes(uint(m.DataSize)) + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfSaveScenarioData) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_savedata.go b/network/mhfpacket/msg_mhf_savedata.go index 76ca02722..556d4f3ab 100644 --- a/network/mhfpacket/msg_mhf_savedata.go +++ b/network/mhfpacket/msg_mhf_savedata.go @@ -6,7 +6,14 @@ import ( ) // MsgMhfSavedata represents the MSG_MHF_SAVEDATA -type MsgMhfSavedata struct{} +type MsgMhfSavedata struct { + AckHandle uint32 + AllocMemSize uint32 + Unk0 uint8 // Either 1 or 2, representing a true or false value for some reason. + Unk1 uint32 + DataSize uint32 + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfSavedata) Opcode() network.PacketID { @@ -15,10 +22,16 @@ func (m *MsgMhfSavedata) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfSavedata) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.AllocMemSize = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint32() + m.DataSize = bf.ReadUint32() + m.RawDataPayload = bf.ReadBytes(uint(m.DataSize)) + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfSavedata) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_set_enhanced_minidata.go b/network/mhfpacket/msg_mhf_set_enhanced_minidata.go index cae548e7c..3ab5ac480 100644 --- a/network/mhfpacket/msg_mhf_set_enhanced_minidata.go +++ b/network/mhfpacket/msg_mhf_set_enhanced_minidata.go @@ -6,7 +6,11 @@ import ( ) // MsgMhfSetEnhancedMinidata represents the MSG_MHF_SET_ENHANCED_MINIDATA -type MsgMhfSetEnhancedMinidata struct{} +type MsgMhfSetEnhancedMinidata struct { + AckHandle uint32 + Unk0 uint16 // Hardcoded 4 in the binary. + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfSetEnhancedMinidata) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgMhfSetEnhancedMinidata) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfSetEnhancedMinidata) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.RawDataPayload = bf.ReadBytes(0x400) + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfSetEnhancedMinidata) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_ack.go b/network/mhfpacket/msg_sys_ack.go index 978e0f5d6..0b8dccad5 100644 --- a/network/mhfpacket/msg_sys_ack.go +++ b/network/mhfpacket/msg_sys_ack.go @@ -8,8 +8,7 @@ import ( // MsgSysAck represents the MSG_SYS_ACK type MsgSysAck struct { AckHandle uint32 - Unk0 uint32 - Unk1 uint32 + AckData []byte } // Opcode returns the ID associated with this packet type. @@ -20,16 +19,12 @@ func (m *MsgSysAck) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysAck) Parse(bf *byteframe.ByteFrame) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint32() - m.Unk1 = bf.ReadUint32() - - return nil + panic("No way to parse without prior context as the packet doesn't include it's own length.") } // Build builds a binary packet from the current data. func (m *MsgSysAck) Build(bf *byteframe.ByteFrame) error { bf.WriteUint32(m.AckHandle) - bf.WriteUint32(m.Unk0) - bf.WriteUint32(m.Unk1) + bf.WriteBytes(m.AckData) return nil } diff --git a/network/mhfpacket/msg_sys_cast_binary.go b/network/mhfpacket/msg_sys_cast_binary.go index 69c9a727b..90667fd36 100644 --- a/network/mhfpacket/msg_sys_cast_binary.go +++ b/network/mhfpacket/msg_sys_cast_binary.go @@ -6,7 +6,13 @@ import ( ) // MsgSysCastBinary represents the MSG_SYS_CAST_BINARY -type MsgSysCastBinary struct{} +type MsgSysCastBinary struct { + Unk0 uint16 + Unk1 uint16 + Type0 uint8 + Type1 uint8 + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgSysCastBinary) Opcode() network.PacketID { @@ -15,10 +21,16 @@ func (m *MsgSysCastBinary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysCastBinary) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() + m.Type0 = bf.ReadUint8() + m.Type1 = bf.ReadUint8() + dataSize := bf.ReadUint16() + m.RawDataPayload = bf.ReadBytes(uint(dataSize)) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysCastBinary) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_casted_binary.go b/network/mhfpacket/msg_sys_casted_binary.go index aeddac73c..d2c0145d4 100644 --- a/network/mhfpacket/msg_sys_casted_binary.go +++ b/network/mhfpacket/msg_sys_casted_binary.go @@ -6,7 +6,12 @@ import ( ) // MsgSysCastedBinary represents the MSG_SYS_CASTED_BINARY -type MsgSysCastedBinary struct{} +type MsgSysCastedBinary struct { + CharID uint32 + Type0 uint8 + Type1 uint8 + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgSysCastedBinary) Opcode() network.PacketID { @@ -20,5 +25,10 @@ func (m *MsgSysCastedBinary) Parse(bf *byteframe.ByteFrame) error { // Build builds a binary packet from the current data. func (m *MsgSysCastedBinary) Build(bf *byteframe.ByteFrame) error { - panic("Not implemented") -} \ No newline at end of file + bf.WriteUint32(m.CharID) + bf.WriteUint8(m.Type0) + bf.WriteUint8(m.Type0) + bf.WriteUint16(uint16(len(m.RawDataPayload))) + bf.WriteBytes(m.RawDataPayload) + return nil +} diff --git a/network/mhfpacket/msg_sys_create_object.go b/network/mhfpacket/msg_sys_create_object.go index 42c23b337..107e982be 100644 --- a/network/mhfpacket/msg_sys_create_object.go +++ b/network/mhfpacket/msg_sys_create_object.go @@ -6,7 +6,11 @@ import ( ) // MsgSysCreateObject represents the MSG_SYS_CREATE_OBJECT -type MsgSysCreateObject struct{} +type MsgSysCreateObject struct { + AckHandle uint32 + X, Y, Z float32 + Unk0 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysCreateObject) Opcode() network.PacketID { @@ -15,10 +19,15 @@ func (m *MsgSysCreateObject) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysCreateObject) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.X = bf.ReadFloat32() + m.Y = bf.ReadFloat32() + m.Z = bf.ReadFloat32() + m.Unk0 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgSysCreateObject) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_end.go b/network/mhfpacket/msg_sys_end.go index f6c874657..07da6c0ce 100644 --- a/network/mhfpacket/msg_sys_end.go +++ b/network/mhfpacket/msg_sys_end.go @@ -15,10 +15,12 @@ func (m *MsgSysEnd) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysEnd) Parse(bf *byteframe.ByteFrame) error { + // No data aside from opcode. return nil } // Build builds a binary packet from the current data. func (m *MsgSysEnd) Build(bf *byteframe.ByteFrame) error { + // No data aside from opcode. return nil } diff --git a/network/mhfpacket/msg_sys_enter_stage.go b/network/mhfpacket/msg_sys_enter_stage.go index 8dd614b92..ee8391988 100644 --- a/network/mhfpacket/msg_sys_enter_stage.go +++ b/network/mhfpacket/msg_sys_enter_stage.go @@ -6,7 +6,12 @@ import ( ) // MsgSysEnterStage represents the MSG_SYS_ENTER_STAGE -type MsgSysEnterStage struct{} +type MsgSysEnterStage struct { + AckHandle uint32 + UnkBool uint8 + StageIDLength uint8 + StageID []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgSysEnterStage) Opcode() network.PacketID { @@ -15,10 +20,14 @@ func (m *MsgSysEnterStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysEnterStage) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.UnkBool = bf.ReadUint8() + m.StageIDLength = bf.ReadUint8() + m.StageID = bf.ReadBytes(uint(m.StageIDLength)) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysEnterStage) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_extend_threshold.go b/network/mhfpacket/msg_sys_extend_threshold.go index f07dc384d..7058b42f8 100644 --- a/network/mhfpacket/msg_sys_extend_threshold.go +++ b/network/mhfpacket/msg_sys_extend_threshold.go @@ -15,10 +15,12 @@ func (m *MsgSysExtendThreshold) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysExtendThreshold) Parse(bf *byteframe.ByteFrame) error { + // No data aside from opcode. return nil } // Build builds a binary packet from the current data. func (m *MsgSysExtendThreshold) Build(bf *byteframe.ByteFrame) error { + // No data aside from opcode. return nil } diff --git a/network/mhfpacket/msg_sys_hide_client.go b/network/mhfpacket/msg_sys_hide_client.go index 95ff37edc..181305088 100644 --- a/network/mhfpacket/msg_sys_hide_client.go +++ b/network/mhfpacket/msg_sys_hide_client.go @@ -6,7 +6,11 @@ import ( ) // MsgSysHideClient represents the MSG_SYS_HIDE_CLIENT -type MsgSysHideClient struct{} +type MsgSysHideClient struct{ + Hide bool + Unk0 uint16 // Hardcoded 0 in binary + Unk1 uint8 // Hardcoded 0 in binary +} // Opcode returns the ID associated with this packet type. func (m *MsgSysHideClient) Opcode() network.PacketID { @@ -15,7 +19,10 @@ func (m *MsgSysHideClient) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysHideClient) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.Hide = bf.ReadBool() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint8() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_sys_nop.go b/network/mhfpacket/msg_sys_nop.go index e769142cd..6b56434a7 100644 --- a/network/mhfpacket/msg_sys_nop.go +++ b/network/mhfpacket/msg_sys_nop.go @@ -15,10 +15,12 @@ func (m *MsgSysNop) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysNop) Parse(bf *byteframe.ByteFrame) error { + // No data aside from opcode. return nil } // Build builds a binary packet from the current data. func (m *MsgSysNop) Build(bf *byteframe.ByteFrame) error { + // No data aside from opcode. return nil } diff --git a/network/mhfpacket/msg_sys_ping.go b/network/mhfpacket/msg_sys_ping.go index ebbfe27ad..90d7343b6 100644 --- a/network/mhfpacket/msg_sys_ping.go +++ b/network/mhfpacket/msg_sys_ping.go @@ -8,7 +8,6 @@ import ( // MsgSysPing represents the MSG_SYS_PING type MsgSysPing struct { AckHandle uint32 - Unk0 uint16 } // Opcode returns the ID associated with this packet type. @@ -19,13 +18,11 @@ func (m *MsgSysPing) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysPing) Parse(bf *byteframe.ByteFrame) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint16() return nil } // Build builds a binary packet from the current data. func (m *MsgSysPing) Build(bf *byteframe.ByteFrame) error { bf.WriteUint32(m.AckHandle) - bf.WriteUint16(m.Unk0) return nil } diff --git a/network/mhfpacket/msg_sys_position_object.go b/network/mhfpacket/msg_sys_position_object.go index 335a526d3..7bb70157c 100644 --- a/network/mhfpacket/msg_sys_position_object.go +++ b/network/mhfpacket/msg_sys_position_object.go @@ -6,7 +6,10 @@ import ( ) // MsgSysPositionObject represents the MSG_SYS_POSITION_OBJECT -type MsgSysPositionObject struct{} +type MsgSysPositionObject struct{ + ObjID uint32 + X, Y, Z float32 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysPositionObject) Opcode() network.PacketID { @@ -15,7 +18,11 @@ func (m *MsgSysPositionObject) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysPositionObject) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.ObjID = bf.ReadUint32() + m.X = bf.ReadFloat32() + m.Y = bf.ReadFloat32() + m.Z = bf.ReadFloat32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_sys_reserve188.go b/network/mhfpacket/msg_sys_reserve188.go index 530ad4e9f..1f6f27860 100644 --- a/network/mhfpacket/msg_sys_reserve188.go +++ b/network/mhfpacket/msg_sys_reserve188.go @@ -6,7 +6,9 @@ import ( ) // MsgSysReserve188 represents the MSG_SYS_reserve188 -type MsgSysReserve188 struct{} +type MsgSysReserve188 struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysReserve188) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgSysReserve188) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysReserve188) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgSysReserve188) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_reserve18b.go b/network/mhfpacket/msg_sys_reserve18b.go index fd9492a13..3874772f8 100644 --- a/network/mhfpacket/msg_sys_reserve18b.go +++ b/network/mhfpacket/msg_sys_reserve18b.go @@ -6,7 +6,9 @@ import ( ) // MsgSysReserve18B represents the MSG_SYS_reserve18B -type MsgSysReserve18B struct{} +type MsgSysReserve18B struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysReserve18B) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgSysReserve18B) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysReserve18B) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgSysReserve18B) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_set_object_binary.go b/network/mhfpacket/msg_sys_set_object_binary.go index 324a7db9d..5c37505fd 100644 --- a/network/mhfpacket/msg_sys_set_object_binary.go +++ b/network/mhfpacket/msg_sys_set_object_binary.go @@ -6,7 +6,11 @@ import ( ) // MsgSysSetObjectBinary represents the MSG_SYS_SET_OBJECT_BINARY -type MsgSysSetObjectBinary struct{} +type MsgSysSetObjectBinary struct { + ObjID uint32 + DataSize uint16 + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgSysSetObjectBinary) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgSysSetObjectBinary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysSetObjectBinary) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.ObjID = bf.ReadUint32() + m.DataSize = bf.ReadUint16() + m.RawDataPayload = bf.ReadBytes(uint(m.DataSize)) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysSetObjectBinary) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_set_user_binary.go b/network/mhfpacket/msg_sys_set_user_binary.go index 8a75b0409..ef6f05fcd 100644 --- a/network/mhfpacket/msg_sys_set_user_binary.go +++ b/network/mhfpacket/msg_sys_set_user_binary.go @@ -6,7 +6,11 @@ import ( ) // MsgSysSetUserBinary represents the MSG_SYS_SET_USER_BINARY -type MsgSysSetUserBinary struct{} +type MsgSysSetUserBinary struct { + BinaryType uint8 + DataSize uint16 + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgSysSetUserBinary) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgSysSetUserBinary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysSetUserBinary) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.BinaryType = bf.ReadUint8() + m.DataSize = bf.ReadUint16() + m.RawDataPayload = bf.ReadBytes(uint(m.DataSize)) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysSetUserBinary) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_terminal_log.go b/network/mhfpacket/msg_sys_terminal_log.go index c34a03a08..e786962ad 100644 --- a/network/mhfpacket/msg_sys_terminal_log.go +++ b/network/mhfpacket/msg_sys_terminal_log.go @@ -5,8 +5,20 @@ import ( "github.com/Andoryuuta/byteframe" ) +// TerminalLogEntry represents an entry in the MSG_SYS_TERMINAL_LOG packet. +type TerminalLogEntry struct { + // Unknown fields + U0, U1, U2, U3, U4, U5, U6, U7, U8 uint32 +} + // MsgSysTerminalLog represents the MSG_SYS_TERMINAL_LOG -type MsgSysTerminalLog struct{} +type MsgSysTerminalLog struct { + AckHandle uint32 + LogID uint32 // 0 on the first packet, and the server sends back a value to use for subsequent requests. + EntryCount uint16 + Unk0 uint16 // Hardcoded 0 in the binary + Entries []*TerminalLogEntry +} // Opcode returns the ID associated with this packet type. func (m *MsgSysTerminalLog) Opcode() network.PacketID { @@ -15,10 +27,29 @@ func (m *MsgSysTerminalLog) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.LogID = bf.ReadUint32() + m.EntryCount = bf.ReadUint16() + m.Unk0 = bf.ReadUint16() + + for i := 0; i < int(m.EntryCount); i++ { + e := &TerminalLogEntry{} + e.U0 = bf.ReadUint32() + e.U1 = bf.ReadUint32() + e.U2 = bf.ReadUint32() + e.U3 = bf.ReadUint32() + e.U4 = bf.ReadUint32() + e.U5 = bf.ReadUint32() + e.U6 = bf.ReadUint32() + e.U7 = bf.ReadUint32() + e.U8 = bf.ReadUint32() + m.Entries = append(m.Entries, e) + } + + return nil } // Build builds a binary packet from the current data. func (m *MsgSysTerminalLog) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_time.go b/network/mhfpacket/msg_sys_time.go index 715a97161..0029395db 100644 --- a/network/mhfpacket/msg_sys_time.go +++ b/network/mhfpacket/msg_sys_time.go @@ -7,8 +7,8 @@ import ( // MsgSysTime represents the MSG_SYS_TIME type MsgSysTime struct { - Unk0 uint8 - Timestamp uint32 // unix timestamp, e.g. 1577105879 + GetRemoteTime bool // Ask the other end to send it's time as well. + Timestamp uint32 // Unix timestamp, e.g. 1577105879 } // Opcode returns the ID associated with this packet type. @@ -18,14 +18,14 @@ func (m *MsgSysTime) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysTime) Parse(bf *byteframe.ByteFrame) error { - m.Unk0 = bf.ReadUint8() + m.GetRemoteTime = bf.ReadBool() m.Timestamp = bf.ReadUint32() return nil } // Build builds a binary packet from the current data. func (m *MsgSysTime) Build(bf *byteframe.ByteFrame) error { - bf.WriteUint8(m.Unk0) + bf.WriteBool(m.GetRemoteTime) bf.WriteUint32(m.Timestamp) return nil } diff --git a/network/mhfpacket/msg_sys_update_right.go b/network/mhfpacket/msg_sys_update_right.go index b3d7d33e2..628bd834f 100644 --- a/network/mhfpacket/msg_sys_update_right.go +++ b/network/mhfpacket/msg_sys_update_right.go @@ -5,8 +5,41 @@ import ( "github.com/Andoryuuta/byteframe" ) +/* +00 58 // Opcode + +00 00 00 00 +00 00 00 4e + +00 04 // Count +00 00 // Skipped(padding?) + +00 01 00 00 00 00 00 00 +00 02 00 00 5d fa 14 c0 +00 03 00 00 5d fa 14 c0 +00 06 00 00 5d e7 05 10 + +00 00 // Count of some buf up to 0x800 bytes following it. + +00 10 // Trailer +*/ + +// ClientRight represents a right that the client has. +type ClientRight struct { + ID uint16 + Unk0 uint16 + Timestamp uint32 +} + // MsgSysUpdateRight represents the MSG_SYS_UPDATE_RIGHT -type MsgSysUpdateRight struct{} +type MsgSysUpdateRight struct { + Unk0 uint32 + Unk1 uint32 + //RightCount uint16 + //Unk3 uint16 // Likely struct padding + Rights []ClientRight + UnkSize uint16 // Count of some buf up to 0x800 bytes following it. +} // Opcode returns the ID associated with this packet type. func (m *MsgSysUpdateRight) Opcode() network.PacketID { @@ -20,5 +53,15 @@ func (m *MsgSysUpdateRight) Parse(bf *byteframe.ByteFrame) error { // Build builds a binary packet from the current data. func (m *MsgSysUpdateRight) Build(bf *byteframe.ByteFrame) error { - panic("Not implemented") -} \ No newline at end of file + bf.WriteUint32(m.Unk0) + bf.WriteUint32(m.Unk1) + bf.WriteUint16(uint16(len(m.Rights))) + bf.WriteUint16(0) // m.Unk3, struct padding. + for _, v := range m.Rights { + bf.WriteUint16(v.ID) + bf.WriteUint16(v.Unk0) + bf.WriteUint32(v.Timestamp) + } + bf.WriteUint16(m.UnkSize) + return nil +} diff --git a/server/channelserver/channel_server.go b/server/channelserver/channel_server.go index 5c43e7e55..0d11080c8 100644 --- a/server/channelserver/channel_server.go +++ b/server/channelserver/channel_server.go @@ -1,19 +1,21 @@ package channelserver import ( - "database/sql" "fmt" "net" "sync" "github.com/Andoryuuta/Erupe/config" + "github.com/Andoryuuta/Erupe/network/mhfpacket" + "github.com/Andoryuuta/byteframe" + "github.com/jmoiron/sqlx" "go.uber.org/zap" ) // Config struct allows configuring the server. type Config struct { Logger *zap.Logger - DB *sql.DB + DB *sqlx.DB ErupeConfig *config.Config } @@ -21,7 +23,7 @@ type Config struct { type Server struct { sync.Mutex logger *zap.Logger - db *sql.DB + db *sqlx.DB erupeConfig *config.Config acceptConns chan net.Conn deleteConns chan net.Conn @@ -29,6 +31,9 @@ type Server struct { listener net.Listener // Listener that is created when Server.Start is called. isShuttingDown bool + + gameObjectLock sync.Mutex + gameObjectCount uint32 } // NewServer creates a new Server type. @@ -117,3 +122,22 @@ func (s *Server) manageSessions() { } } } + +// BroadcastMHF queues a MHFPacket to be sent to all sessions. +func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) { + // Make the header + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(pkt.Opcode())) + + // Build the packet onto the byteframe. + pkt.Build(bf) + + // Broadcast the data. + for _, session := range s.sessions { + if session == ignoredSession { + continue + } + // Enqueue in a non-blocking way that drops the packet if the connections send buffer channel is full. + session.QueueSendNonBlocking(bf.Data()) + } +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index de185f0f1..8cb2f427f 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -1,11 +1,66 @@ package channelserver import ( - "github.com/Andoryuuta/Erupe/network" + "bytes" + "fmt" + "io/ioutil" + "log" + "time" + "github.com/Andoryuuta/Erupe/network/mhfpacket" "github.com/Andoryuuta/byteframe" + "golang.org/x/text/encoding/japanese" + "golang.org/x/text/transform" ) +// Temporary function to just return no results for a MSG_MHF_ENUMERATE* packet +func stubEnumerateNoResults(s *Session, ackHandle uint32) { + enumBf := byteframe.NewByteFrame() + enumBf.WriteUint16(0) // Entry count (count for quests, rankings, events, etc.) + + doSizedAckResp(s, ackHandle, enumBf.Data()) +} + +// Temporary function to just return no results for many MSG_MHF_GET* packets. +func stubGetNoResults(s *Session, ackHandle uint32) { + resp := byteframe.NewByteFrame() + resp.WriteUint32(0x0A218EAD) // Unk shared ID. Sent in response of MSG_MHF_GET_TOWER_INFO, MSG_MHF_GET_PAPER_DATA etc. (World ID?) + resp.WriteUint32(0) // Unk + resp.WriteUint32(0) // Unk + resp.WriteUint32(0) // Entry count + + doSizedAckResp(s, ackHandle, resp.Data()) +} + +// Some common ACK response header that a lot (but not all) of the packet responses use. +func doSizedAckResp(s *Session, ackHandle uint32, data []byte) { + // Wrap the data into another container with the data size. + bfw := byteframe.NewByteFrame() + bfw.WriteUint8(1) // Unk + bfw.WriteUint8(0) // Unk + bfw.WriteUint16(uint16(len(data))) // Data size + if len(data) > 0 { + bfw.WriteBytes(data) + } + + s.QueueAck(ackHandle, bfw.Data()) +} + +func fixedSizeShiftJIS(text string, size int) []byte { + r := bytes.NewBuffer([]byte(text)) + encoded, err := ioutil.ReadAll(transform.NewReader(r, japanese.ShiftJIS.NewEncoder())) + if err != nil { + panic(err) + } + + out := make([]byte, size) + copy(out, encoded) + + // Null terminate it. + out[len(out)-1] = 0 + return out +} + func handleMsgHead(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysReserve01(s *Session, p mhfpacket.MHFPacket) {} @@ -36,24 +91,45 @@ func handleMsgSysReserve0D(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysReserve0E(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysExtendThreshold(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysExtendThreshold(s *Session, p mhfpacket.MHFPacket) { + // No data aside from header, no resp required. +} -func handleMsgSysEnd(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysEnd(s *Session, p mhfpacket.MHFPacket) { + // No data aside from header, no resp required. +} -func handleMsgSysNop(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysNop(s *Session, p mhfpacket.MHFPacket) { + // No data aside from header, no resp required. +} func handleMsgSysAck(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysTerminalLog(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysTerminalLog(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysTerminalLog) + + resp := byteframe.NewByteFrame() + /* + if pkt.LogID == 0{ + fmt.Println("New log session") + } + */ + resp.WriteUint32(0) // UNK + resp.WriteUint32(0x98bd51a9) // LogID to use for requests after this. + s.QueueAck(pkt.AckHandle, resp.Data()) +} func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysLogin) + s.Lock() + s.charID = pkt.CharID0 + s.Unlock() + bf := byteframe.NewByteFrame() - bf.WriteUint16(uint16(network.MSG_SYS_ACK)) - bf.WriteUint32(pkt.AckHandle) - bf.WriteUint64(0x000000005E00B9C2) // Timestamp? - s.cryptConn.SendPacket(bf.Data()) + bf.WriteUint32(0) // Unk + bf.WriteUint32(uint32(time.Now().Unix())) // Unix timestamp + s.QueueAck(pkt.AckHandle, bf.Data()) } func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) {} @@ -64,31 +140,72 @@ func handleMsgSysPing(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysPing) bf := byteframe.NewByteFrame() - bf.WriteUint16(uint16(network.MSG_SYS_ACK)) - ack := mhfpacket.MsgSysAck{ - AckHandle: pkt.AckHandle, - Unk0: 0, - Unk1: 0, - } - ack.Build(bf) - s.cryptConn.SendPacket(bf.Data()) + bf.WriteUint32(0) // Unk + bf.WriteUint32(0) // Unk + s.QueueAck(pkt.AckHandle, bf.Data()) } -func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysCastBinary) -func handleMsgSysHideClient(s *Session, p mhfpacket.MHFPacket) {} + if pkt.Type0 == 3 && pkt.Type1 == 1 { + fmt.Println("Got chat message!") + + resp := &mhfpacket.MsgSysCastedBinary{ + CharID: s.charID, + Type0: 1, + Type1: 1, + RawDataPayload: pkt.RawDataPayload, + } + s.server.BroadcastMHF(resp, s) + + /* + // Made the inside of the casted binary + payload := byteframe.NewByteFrame() + payload.WriteUint16(uint16(i)) // Chat type + + //Chat type 0 = World + //Chat type 1 = Local + //Chat type 2 = Guild + //Chat type 3 = Alliance + //Chat type 4 = Party + //Chat type 5 = Whisper + //Thanks to @Alice on discord for identifying these. + + payload.WriteUint8(0) // Unknown + msg := fmt.Sprintf("Chat type %d", i) + playername := fmt.Sprintf("Ando") + payload.WriteUint16(uint16(len(playername) + 1)) + payload.WriteUint16(uint16(len(msg) + 1)) + payload.WriteUint8(0) // Is this correct, or do I have the endianess of the prev 2 fields wrong? + payload.WriteNullTerminatedBytes([]byte(msg)) + payload.WriteNullTerminatedBytes([]byte(playername)) + payloadBytes := payload.Data() + + //Wrap it in a CASTED_BINARY packet to broadcast + bfw := byteframe.NewByteFrame() + bfw.WriteUint16(uint16(network.MSG_SYS_CASTED_BINARY)) + bfw.WriteUint32(0x23325A29) // Character ID + bfw.WriteUint8(1) // type + bfw.WriteUint8(1) // type2 + bfw.WriteUint16(uint16(len(payloadBytes))) + bfw.WriteBytes(payloadBytes) + */ + } +} + +func handleMsgSysHideClient(s *Session, p mhfpacket.MHFPacket) { + //pkt := p.(*mhfpacket.MsgSysHideClient) +} func handleMsgSysTime(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgSysTime) + //pkt := p.(*mhfpacket.MsgSysTime) - bf := byteframe.NewByteFrame() - bf.WriteUint16(uint16(network.MSG_SYS_TIME)) - resp := mhfpacket.MsgSysTime{ - Unk0: pkt.Unk0, - Timestamp: pkt.Timestamp, + resp := &mhfpacket.MsgSysTime{ + GetRemoteTime: false, + Timestamp: uint32(time.Now().Unix()), } - resp.Build(bf) - s.cryptConn.SendPacket(bf.Data()) + s.QueueSendMHF(resp) } func handleMsgSysCastedBinary(s *Session, p mhfpacket.MHFPacket) {} @@ -105,7 +222,17 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysStageDestruct(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysEnterStage) + + s.Lock() + s.stageID = string(pkt.StageID) + s.Unlock() + + //TODO: Send MSG_SYS_CLEANUP_OBJECT here before the client changes stages. + + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {} @@ -165,17 +292,37 @@ func handleMsgSysLoadRegister(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysNotifyRegister(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysCreateObject) + + // Make a new object ID. + s.server.gameObjectLock.Lock() + objID := s.server.gameObjectCount + s.server.gameObjectCount++ + s.server.gameObjectLock.Unlock() + + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) // Unk, is this echoed back from pkt.Unk0? + resp.WriteUint32(objID) // New local obj handle. + + s.QueueAck(pkt.AckHandle, resp.Data()) +} func handleMsgSysDeleteObject(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysPositionObject(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysPositionObject(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysPositionObject) + fmt.Printf("Moved object %v to (%f,%f,%f)\n", pkt.ObjID, pkt.X, pkt.Y, pkt.Z) + +} func handleMsgSysRotateObject(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysDuplicateObject(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) { + +} func handleMsgSysGetObjectBinary(s *Session, p mhfpacket.MHFPacket) {} @@ -201,7 +348,9 @@ func handleMsgSysInsertUser(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysDeleteUser(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) { + //pkt := p.(*mhfpacket.MsgSysSetUserBinary) +} func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) {} @@ -229,7 +378,14 @@ func handleMsgSysReserve5E(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysReserve5F(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfSavedata) + err := ioutil.WriteFile(fmt.Sprintf("savedata\\%d.bin", time.Now().Unix()), pkt.RawDataPayload, 0644) + if err != nil { + log.Fatal(err) + } + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) {} @@ -253,7 +409,11 @@ func handleMsgMhfListMail(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfOprtMail(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfLoadFavoriteQuest(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfLoadFavoriteQuest(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoadFavoriteQuest) + // TODO(Andoryuuta): Save data from MsgMhfSaveFavoriteQuest and resend it here. + doSizedAckResp(s, pkt.AckHandle, []byte{}) +} func handleMsgMhfSaveFavoriteQuest(s *Session, p mhfpacket.MHFPacket) {} @@ -357,17 +517,35 @@ func handleMsgMhfMercenaryHuntdata(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfEntryRookieGuild(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateQuest) + stubEnumerateNoResults(s, pkt.AckHandle) +} -func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateEvent) + stubEnumerateNoResults(s, pkt.AckHandle) +} -func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumeratePrice) + stubEnumerateNoResults(s, pkt.AckHandle) +} -func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateRanking) + stubEnumerateNoResults(s, pkt.AckHandle) +} -func handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateOrder) + stubEnumerateNoResults(s, pkt.AckHandle) +} -func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumerateShop(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateShop) + stubEnumerateNoResults(s, pkt.AckHandle) +} func handleMsgMhfGetExtraInfo(s *Session, p mhfpacket.MHFPacket) {} @@ -405,7 +583,12 @@ func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfInfoFesta) + + // REALLY large/complex format... stubbing it out here for simplicity. + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) {} @@ -441,7 +624,12 @@ func handleMsgMhfCreateMercenary(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfSaveMercenary(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfReadMercenaryW(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfReadMercenaryW) + + // Unk format: + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfReadMercenaryM(s *Session, p mhfpacket.MHFPacket) {} @@ -473,15 +661,31 @@ func handleMsgMhfDisplayedAchievement(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfInfoScenarioCounter(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfSaveScenarioData) + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40}) +} -func handleMsgMhfLoadScenarioData(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfLoadScenarioData(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoadScenarioData) + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfGetBbsSnsStatus(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfApplyBbsArticle(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetEtcPoints(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetEtcPoints(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetEtcPoints) + + resp := byteframe.NewByteFrame() + resp.WriteUint8(0x3) // Maybe a count of uint32(s)? + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfUpdateEtcPoint(s *Session, p mhfpacket.MHFPacket) {} @@ -489,7 +693,32 @@ func handleMsgMhfGetMyhouseInfo(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUpdateMyhouseInfo(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetWeeklySchedule) + + eventSchedules := []struct { + StartTime time.Time + Unk0 uint32 // Event ID? + Unk1 uint16 + }{ + { + StartTime: time.Now().Add(time.Duration(-5) * time.Minute), // Event started 5 minutes ago. + Unk0: 4, + Unk1: 0, + }, + } + + resp := byteframe.NewByteFrame() + resp.WriteUint8(uint8(len(eventSchedules))) // Entry count, client only parses the first 7 or 8. + resp.WriteUint32(uint32(time.Now().Unix())) // Current server time + for _, es := range eventSchedules { + resp.WriteUint32(uint32(es.StartTime.Unix())) + resp.WriteUint32(es.Unk0) + resp.WriteUint16(es.Unk1) + } + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfEnumerateInvGuild(s *Session, p mhfpacket.MHFPacket) {} @@ -501,19 +730,56 @@ func handleMsgMhfStampcardPrize(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUnreserveSrg(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfLoadPlateData(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfLoadPlateData(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoadPlateData) + + // TODO(Andoryuuta): Save data from MsgMhfSavePlateData and resend it here. + doSizedAckResp(s, pkt.AckHandle, []byte{}) +} func handleMsgMhfSavePlateData(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfLoadPlateBox(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfLoadPlateBox(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoadPlateBox) + // TODO(Andoryuuta): Save data from MsgMhfSavePlateBox and resend it here. + doSizedAckResp(s, pkt.AckHandle, []byte{}) +} func handleMsgMhfSavePlateBox(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfReadGuildcard(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfReadGuildcard(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfReadGuildcard) + + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfUpdateGuildcard(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfReadBeatLevel(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfReadBeatLevel(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfReadBeatLevel) + + // This response is fixed and will never change on JP, + // but I've left it dynamic for possible other client differences. + resp := byteframe.NewByteFrame() + for i := 0; i < int(pkt.ValidIDCount); i++ { + resp.WriteUint32(pkt.IDs[i]) + resp.WriteUint32(1) + resp.WriteUint32(1) + resp.WriteUint32(1) + } + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfUpdateBeatLevel(s *Session, p mhfpacket.MHFPacket) {} @@ -537,11 +803,32 @@ func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetWeeklySeibatuRankingReward(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetEarthStatus) -func handleMsgMhfLoadPartner(s *Session, p mhfpacket.MHFPacket) {} + // TODO(Andoryuuta): Track down format for this data, + // it can somehow be parsed as 8*uint32 chunks if the header is right. + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) + resp.WriteUint32(0) -func handleMsgMhfSavePartner(s *Session, p mhfpacket.MHFPacket) {} + s.QueueAck(pkt.AckHandle, resp.Data()) +} + +func handleMsgMhfLoadPartner(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoadPartner) + + // TODO(Andoryuuta): Figure out unusual double ack. One sized, one not. + + // TODO(Andoryuuta): Save data from MsgMhfSavePartner and resend it here. + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} + +func handleMsgMhfSavePartner(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfSavePartner) + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfGetGuildMissionList(s *Session, p mhfpacket.MHFPacket) {} @@ -553,7 +840,12 @@ func handleMsgMhfSetGuildMissionTarget(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCancelGuildMissionTarget(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfLoadOtomoAirou(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfLoadOtomoAirou(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoadOtomoAirou) + + // TODO(Andoryuuta): Save data from MsgMhfSaveOtomoAirou and resend it here. + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfSaveOtomoAirou(s *Session, p mhfpacket.MHFPacket) {} @@ -575,7 +867,12 @@ func handleMsgMhfEnumerateFestaIntermediatePrize(s *Session, p mhfpacket.MHFPack func handleMsgMhfAcquireFestaIntermediatePrize(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfLoadDecoMyset(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfLoadDecoMyset(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoadDecoMyset) + + // TODO(Andoryuuta): Save data from MsgMhfSaveDecoMyset and resend it here. + doSizedAckResp(s, pkt.AckHandle, []byte{0x01, 0x00}) +} func handleMsgMhfSaveDecoMyset(s *Session, p mhfpacket.MHFPacket) {} @@ -595,7 +892,12 @@ func handleMsgMhfChargeGuildAdventure(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfLoadLegendDispatch(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoadHunterNavi) + // TODO(Andoryuuta): Save data from MsgMhfSaveHunterNavi and resend it here. + blankData := make([]byte, 0x228) + doSizedAckResp(s, pkt.AckHandle, blankData) +} func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {} @@ -607,7 +909,46 @@ func handleMsgMhfGetGuildWeeklyBonusActiveCount(s *Session, p mhfpacket.MHFPacke func handleMsgMhfAddGuildWeeklyBonusExceptionalUser(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetTowerInfo) + + /* + type: + 1 == TOWER_RANK_POINT, + 2 == GET_OWN_TOWER_SKILL + 3 == ? + 4 == TOWER_TOUHA_HISTORY + 5 = ? + + [] = type + req + resp + + 01 1d 01 fc 00 09 [00 00 00 01] 00 00 00 02 00 00 00 00 + 00 12 01 fc 00 09 01 00 00 18 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 + + 01 1d 01 fc 00 0a [00 00 00 02] 00 00 00 00 00 00 00 00 + 00 12 01 fc 00 0a 01 00 00 94 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + 01 1d 01 ff 00 0f [00 00 00 04] 00 00 00 00 00 00 00 00 + 00 12 01 ff 00 0f 01 00 00 24 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + + 01 1d 01 fc 00 0b [00 00 00 05] 00 00 00 00 00 00 00 00 + 00 12 01 fc 00 0b 01 00 00 10 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 00 + */ + /* + switch pkt.InfoType { + case mhfpacket.TowerInfoTypeTowerRankPoint: + case mhfpacket.TowerInfoTypeGetOwnTowerSkill: + case mhfpacket.TowerInfoTypeUnk3: + panic("No known response values for TowerInfoTypeUnk3") + case mhfpacket.TowerInfoTypeTowerTouhaHistory: + case mhfpacket.TowerInfoTypeUnk5: + } + */ + + stubGetNoResults(s, pkt.AckHandle) +} func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) {} @@ -615,21 +956,92 @@ func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetEarthValue(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetEarthValue(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetEarthValue) + + earthValues := []struct { + Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32 + }{ + { + Unk0: 0x03E9, + Unk1: 0x5B, + }, + { + Unk0: 0x2329, + Unk1: 0x03, + }, + { + Unk0: 0x232A, + Unk1: 0x0A, + Unk2: 0x012C, + }, + } + + resp := byteframe.NewByteFrame() + resp.WriteUint32(0x0A218EAD) // Unk shared ID. Sent in response of MSG_MHF_GET_TOWER_INFO, MSG_MHF_GET_PAPER_DATA etc. + resp.WriteUint32(0) // Unk + resp.WriteUint32(0) // Unk + resp.WriteUint32(uint32(len(earthValues))) // value count + for _, v := range earthValues { + resp.WriteUint32(v.Unk0) + resp.WriteUint32(v.Unk1) + resp.WriteUint32(v.Unk2) + resp.WriteUint32(v.Unk3) + resp.WriteUint32(v.Unk4) + resp.WriteUint32(v.Unk5) + } + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfDebugPostValue(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetPaperData(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetPaperData(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetPaperData) + stubGetNoResults(s, pkt.AckHandle) +} func handleMsgMhfGetNotice(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfPostNotice(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetBoostTime) + doSizedAckResp(s, pkt.AckHandle, []byte{}) + + // Update the client's rights as well: + update := &mhfpacket.MsgSysUpdateRight{ + Unk0: 0, + Unk1: 0x4E, + Rights: []mhfpacket.ClientRight{ + { + ID: 1, + Timestamp: 0, + }, + { + ID: 2, + Timestamp: 0x5dfa14c0, + }, + { + ID: 3, + Timestamp: 0x5dfa14c0, + }, + { + ID: 6, + Timestamp: 0x5de70510, + }, + }, + UnkSize: 0, + } + s.QueueSendMHF(update) +} func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit) + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {} @@ -647,7 +1059,10 @@ func handleMsgMhfReceiveCafeDurationBonus(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfPostCafeDurationBonusReceived(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetGachaPoint(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetGachaPoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetGachaPoint) + doSizedAckResp(s, pkt.AckHandle, []byte{}) +} func handleMsgMhfUseGachaPoint(s *Session, p mhfpacket.MHFPacket) {} @@ -673,7 +1088,10 @@ func handleMsgMhfGetSenyuDailyCount(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetGuildTargetMemberNum(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetBoostRight) + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {} @@ -727,13 +1145,101 @@ func handleMsgMhfGetCaAchievementHist(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfSetCaAchievementHist(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetKeepLoginBoostStatus(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetKeepLoginBoostStatus(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetKeepLoginBoostStatus) + + unkRespFields := [5]struct { + U0, U1, U2 uint8 + U3 uint32 + }{ + { + U0: 1, + U1: 1, + U2: 1, + U3: 0, + }, + { + U0: 2, + U1: 0, + U2: 1, + U3: 0, + }, + { + U0: 3, + U1: 0, + U2: 1, + U3: 0, + }, + { + U0: 4, + U1: 0, + U2: 1, + U3: 0, + }, + { + U0: 5, + U1: 0, + U2: 1, + U3: 0, + }, + } + + resp := byteframe.NewByteFrame() + for _, v := range unkRespFields { + resp.WriteUint8(v.U0) + resp.WriteUint8(v.U1) + resp.WriteUint8(v.U2) + resp.WriteUint32(v.U3) + } + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfUseKeepLoginBoost(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdSchedule) -func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) {} + resp := byteframe.NewByteFrame() + resp.WriteUint32(0x0b5397df) // Unk + resp.WriteUint32(0x5ddde6b0) // Timestamp + resp.WriteUint32(0x5de71320) // Timestamp + resp.WriteUint32(0x5de7225c) // Timestamp + resp.WriteUint32(0x5df04da0) // Timestamp + resp.WriteUint32(0x5df05cdc) // Timestamp + resp.WriteUint32(0x5dfa30e0) // Timestamp + resp.WriteUint16(0x19) // Unk + resp.WriteUint16(0x2d) // Unk + resp.WriteUint16(0x02) // Unk + resp.WriteUint16(0x02) // Unk + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} + +func handleMsgMhfGetUdInfo(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdInfo) + + udInfos := []struct { + Text string + StartTime time.Time + EndTime time.Time + }{ + { + Text: " ~C17【Erupe】 launch event!\n\n■Features\n~C18 Walk around!\n~C17 Crash your connection by doing nearly anything!", + StartTime: time.Now().Add(time.Duration(-5) * time.Minute), // Event started 5 minutes ago, + EndTime: time.Now().Add(time.Duration(5) * time.Minute), // Event ends in 5 minutes, + }, + } + + resp := byteframe.NewByteFrame() + resp.WriteUint8(uint8(len(udInfos))) + for _, udInfo := range udInfos { + resp.WriteBytes(fixedSizeShiftJIS(udInfo.Text, 1024)) + resp.WriteUint32(uint32(udInfo.StartTime.Unix())) + resp.WriteUint32(uint32(udInfo.EndTime.Unix())) + } + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfGetKijuInfo(s *Session, p mhfpacket.MHFPacket) {} @@ -749,7 +1255,137 @@ func handleMsgMhfGetUdBonusQuestInfo(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdSelectedColorInfo(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetUdMonsterPoint(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetUdMonsterPoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdMonsterPoint) + + monsterPoints := []struct { + MID uint8 // Monster ID ? + Points uint16 + }{ + {MID: 0x01, Points: 0x3C}, + {MID: 0x02, Points: 0x5A}, + {MID: 0x06, Points: 0x14}, + {MID: 0x07, Points: 0x50}, + {MID: 0x08, Points: 0x28}, + {MID: 0x0B, Points: 0x3C}, + {MID: 0x0E, Points: 0x3C}, + {MID: 0x0F, Points: 0x46}, + {MID: 0x11, Points: 0x46}, + {MID: 0x14, Points: 0x28}, + {MID: 0x15, Points: 0x3C}, + {MID: 0x16, Points: 0x32}, + {MID: 0x1A, Points: 0x32}, + {MID: 0x1B, Points: 0x0A}, + {MID: 0x1C, Points: 0x0A}, + {MID: 0x1F, Points: 0x0A}, + {MID: 0x21, Points: 0x50}, + {MID: 0x24, Points: 0x64}, + {MID: 0x25, Points: 0x3C}, + {MID: 0x26, Points: 0x1E}, + {MID: 0x27, Points: 0x28}, + {MID: 0x28, Points: 0x50}, + {MID: 0x29, Points: 0x5A}, + {MID: 0x2A, Points: 0x50}, + {MID: 0x2B, Points: 0x3C}, + {MID: 0x2C, Points: 0x3C}, + {MID: 0x2D, Points: 0x46}, + {MID: 0x2E, Points: 0x3C}, + {MID: 0x2F, Points: 0x50}, + {MID: 0x30, Points: 0x1E}, + {MID: 0x31, Points: 0x3C}, + {MID: 0x32, Points: 0x50}, + {MID: 0x33, Points: 0x3C}, + {MID: 0x34, Points: 0x28}, + {MID: 0x35, Points: 0x50}, + {MID: 0x36, Points: 0x6E}, + {MID: 0x37, Points: 0x50}, + {MID: 0x3A, Points: 0x50}, + {MID: 0x3B, Points: 0x6E}, + {MID: 0x40, Points: 0x64}, + {MID: 0x41, Points: 0x6E}, + {MID: 0x43, Points: 0x28}, + {MID: 0x44, Points: 0x0A}, + {MID: 0x47, Points: 0x6E}, + {MID: 0x4A, Points: 0xFA}, + {MID: 0x4B, Points: 0xFA}, + {MID: 0x4C, Points: 0x46}, + {MID: 0x4D, Points: 0x64}, + {MID: 0x4E, Points: 0xFA}, + {MID: 0x4F, Points: 0xFA}, + {MID: 0x50, Points: 0xFA}, + {MID: 0x51, Points: 0xFA}, + {MID: 0x52, Points: 0xFA}, + {MID: 0x53, Points: 0xFA}, + {MID: 0x54, Points: 0xFA}, + {MID: 0x55, Points: 0xFA}, + {MID: 0x59, Points: 0xFA}, + {MID: 0x5A, Points: 0xFA}, + {MID: 0x5B, Points: 0xFA}, + {MID: 0x5C, Points: 0xFA}, + {MID: 0x5E, Points: 0xFA}, + {MID: 0x5F, Points: 0xFA}, + {MID: 0x60, Points: 0xFA}, + {MID: 0x63, Points: 0xFA}, + {MID: 0x65, Points: 0xFA}, + {MID: 0x67, Points: 0xFA}, + {MID: 0x68, Points: 0xFA}, + {MID: 0x69, Points: 0xFA}, + {MID: 0x6A, Points: 0xFA}, + {MID: 0x6B, Points: 0xFA}, + {MID: 0x6C, Points: 0xFA}, + {MID: 0x6D, Points: 0xFA}, + {MID: 0x6E, Points: 0xFA}, + {MID: 0x6F, Points: 0xFA}, + {MID: 0x70, Points: 0xFA}, + {MID: 0x72, Points: 0xFA}, + {MID: 0x73, Points: 0xFA}, + {MID: 0x74, Points: 0xFA}, + {MID: 0x77, Points: 0xFA}, + {MID: 0x78, Points: 0xFA}, + {MID: 0x79, Points: 0xFA}, + {MID: 0x7A, Points: 0xFA}, + {MID: 0x7B, Points: 0xFA}, + {MID: 0x7D, Points: 0xFA}, + {MID: 0x7E, Points: 0xFA}, + {MID: 0x7F, Points: 0xFA}, + {MID: 0x80, Points: 0xFA}, + {MID: 0x81, Points: 0xFA}, + {MID: 0x82, Points: 0xFA}, + {MID: 0x83, Points: 0xFA}, + {MID: 0x8B, Points: 0xFA}, + {MID: 0x8C, Points: 0xFA}, + {MID: 0x8D, Points: 0xFA}, + {MID: 0x8E, Points: 0xFA}, + {MID: 0x90, Points: 0xFA}, + {MID: 0x92, Points: 0x78}, + {MID: 0x93, Points: 0x78}, + {MID: 0x94, Points: 0x78}, + {MID: 0x96, Points: 0xFA}, + {MID: 0x97, Points: 0x78}, + {MID: 0x98, Points: 0x78}, + {MID: 0x99, Points: 0x78}, + {MID: 0x9A, Points: 0xFA}, + {MID: 0x9E, Points: 0xFA}, + {MID: 0x9F, Points: 0x78}, + {MID: 0xA0, Points: 0xFA}, + {MID: 0xA1, Points: 0xFA}, + {MID: 0xA2, Points: 0x78}, + {MID: 0xA4, Points: 0x78}, + {MID: 0xA5, Points: 0x78}, + {MID: 0xA6, Points: 0xFA}, + {MID: 0xA9, Points: 0x78}, + {MID: 0xAA, Points: 0xFA}, + } + + resp := byteframe.NewByteFrame() + resp.WriteUint8(uint8(len(monsterPoints))) + for _, mp := range monsterPoints { + resp.WriteUint8(mp.MID) + resp.WriteUint16(mp.Points) + } + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfGetUdDailyPresentList(s *Session, p mhfpacket.MHFPacket) {} @@ -769,7 +1405,14 @@ func handleMsgMhfGetUdRanking(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdMyRanking(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfAcquireMonthlyReward(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfAcquireMonthlyReward(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfAcquireMonthlyReward) + + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfGetUdGuildMapInfo(s *Session, p mhfpacket.MHFPacket) {} @@ -797,9 +1440,14 @@ func handleMsgMhfGetUdShopCoin(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUseUdShopCoin(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetEnhancedMinidata) + doSizedAckResp(s, pkt.AckHandle, []byte{0x00}) +} -func handleMsgMhfSetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfSetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) { + +} func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) {} @@ -811,7 +1459,10 @@ func handleMsgMhfGuildHuntdata(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfAddKouryouPoint(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetKouryouPoint(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetKouryouPoint(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetKouryouPoint) + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfExchangeKouryouPoint(s *Session, p mhfpacket.MHFPacket) {} @@ -821,13 +1472,29 @@ func handleMsgMhfGetUdTacticsFirstQuestBonus(s *Session, p mhfpacket.MHFPacket) func handleMsgMhfGetUdTacticsRemainingPoint(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysReserve188(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysReserve188(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysReserve188) -func handleMsgMhfLoadPlateMyset(s *Session, p mhfpacket.MHFPacket) {} + // Left as raw bytes because I couldn't easily find the request or resp parser function in the binary. + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) +} + +func handleMsgMhfLoadPlateMyset(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoadPlateMyset) + // TODO(Andoryuuta): Save data from MsgMhfSavePlateMyset and resend it here. + blankData := make([]byte, 0x780) + doSizedAckResp(s, pkt.AckHandle, blankData) +} func handleMsgMhfSavePlateMyset(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysReserve18B(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysReserve18B(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysReserve18B) + + // Left as raw bytes because I couldn't easily find the request or resp parser function in the binary. + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x3C}) + +} func handleMsgMhfGetRestrictionEvent(s *Session, p mhfpacket.MHFPacket) {} @@ -849,7 +1516,47 @@ func handleMsgSysReserve194(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfSaveRengokuData(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfLoadRengokuData(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfLoadRengokuData(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoadRengokuData) + + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint16(0) + resp.WriteUint32(0) + resp.WriteUint16(0) + resp.WriteUint16(0) + resp.WriteUint32(0) + + resp.WriteUint8(3) // Count of next 3 + resp.WriteUint16(0) + resp.WriteUint16(0) + resp.WriteUint16(0) + + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + + resp.WriteUint8(3) // Count of next 3 + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + + resp.WriteUint8(3) // Count of next 3 + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) + +} func handleMsgMhfGetRengokuBinary(s *Session, p mhfpacket.MHFPacket) {} @@ -858,20 +1565,35 @@ func handleMsgMhfEnumerateRengokuRanking(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetRengokuRankingRank(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetRengokuRankingRank) - bf := byteframe.NewByteFrame() - bf.WriteUint16(uint16(network.MSG_SYS_ACK)) - bf.WriteUint32(pkt.AckHandle) - bf.WriteBytes([]byte{0x01, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - s.cryptConn.SendPacket(bf.Data()) + resp := byteframe.NewByteFrame() + resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) } func handleMsgMhfAcquireExchangeShop(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysReserve19B(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfSaveMezfesData(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfSaveMezfesData(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfSaveMezfesData) + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} -func handleMsgMhfLoadMezfesData(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfLoadMezfesData(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoadMezfesData) + + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) // Unk + + resp.WriteUint8(2) // Count of the next 2 uint32s + resp.WriteUint32(0) + resp.WriteUint32(0) + + resp.WriteUint32(0) // Unk + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgSysReserve19E(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/session.go b/server/channelserver/session.go index 0e0a0e079..1024df74b 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -3,7 +3,6 @@ package channelserver import ( "encoding/hex" "fmt" - "io/ioutil" "net" "sync" @@ -16,48 +15,104 @@ import ( // Session holds state for the channel server connection. type Session struct { sync.Mutex - logger *zap.Logger - server *Server - rawConn net.Conn - cryptConn *network.CryptConn + logger *zap.Logger + server *Server + rawConn net.Conn + cryptConn *network.CryptConn + sendPackets chan []byte + + stageID string + charID uint32 } // NewSession creates a new Session type. func NewSession(server *Server, conn net.Conn) *Session { s := &Session{ - logger: server.logger, - server: server, - rawConn: conn, - cryptConn: network.NewCryptConn(conn), + logger: server.logger, + server: server, + rawConn: conn, + cryptConn: network.NewCryptConn(conn), + sendPackets: make(chan []byte, 20), } return s } -// Start starts the session packet read&handle loop. +// Start starts the session packet send and recv loop(s). func (s *Session) Start() { go func() { - s.logger.Info("Channel server got connection!") + s.logger.Info("Channel server got connection!", zap.String("remoteaddr", s.rawConn.RemoteAddr().String())) // Unlike the sign and entrance server, // the client DOES NOT initalize the channel connection with 8 NULL bytes. - - for { - pkt, err := s.cryptConn.ReadPacket() - if err != nil { - s.logger.Warn("Error on channel server readpacket", zap.Error(err)) - return - } - - s.handlePacketGroup(pkt) - - } + go s.sendLoop() + s.recvLoop() }() } -var loadDataCount int -var getPaperDataCount int +// QueueSend queues a packet (raw []byte) to be sent. +func (s *Session) QueueSend(data []byte) { + s.sendPackets <- data +} + +// QueueSendNonBlocking queues a packet (raw []byte) to be sent, dropping the packet entirely if the queue is full. +func (s *Session) QueueSendNonBlocking(data []byte) { + select { + case s.sendPackets <- data: + // Enqueued properly. + default: + // Couldn't enqueue, likely something wrong with the connection. + s.logger.Warn("Dropped packet for session because of full send buffer, something is probably wrong") + } +} + +// QueueSendMHF queues a MHFPacket to be sent. +func (s *Session) QueueSendMHF(pkt mhfpacket.MHFPacket) { + // Make the header + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(pkt.Opcode())) + + // Build the packet onto the byteframe. + pkt.Build(bf) + + // Queue it. + s.QueueSend(bf.Data()) +} + +// QueueAck is a helper function to queue an MSG_SYS_ACK with the given ack handle and data. +func (s *Session) QueueAck(ackHandle uint32, data []byte) { + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(network.MSG_SYS_ACK)) + bf.WriteUint32(ackHandle) + bf.WriteBytes(data) + s.QueueSend(bf.Data()) +} + +func (s *Session) sendLoop() { + for { + // TODO(Andoryuuta): Test making this into a buffered channel and grouping the packet together before sending. + rawPacket := <-s.sendPackets + if rawPacket == nil { + s.logger.Debug("Got nil from s.SendPackets, exiting send loop") + return + } + s.cryptConn.SendPacket(rawPacket) + } +} + +func (s *Session) recvLoop() { + for { + pkt, err := s.cryptConn.ReadPacket() + if err != nil { + s.logger.Warn("Error on ReadPacket, exiting recv loop", zap.Error(err)) + return + } + + s.handlePacketGroup(pkt) + } +} func (s *Session) handlePacketGroup(pktGroup []byte) { + // This shouldn't be needed, but it's better to recover and let the connection die than to panic the server. defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic.") @@ -67,370 +122,30 @@ func (s *Session) handlePacketGroup(pktGroup []byte) { bf := byteframe.NewByteFrameFromBytes(pktGroup) opcode := network.PacketID(bf.ReadUint16()) - if opcode != network.MSG_SYS_END { + // Print any (non-common spam) packet opcodes and data. + if opcode != network.MSG_SYS_END && + opcode != network.MSG_SYS_PING && + opcode != network.MSG_SYS_NOP && + opcode != network.MSG_SYS_TIME && + opcode != network.MSG_SYS_EXTEND_THRESHOLD { fmt.Printf("Opcode: %s\n", opcode) fmt.Printf("Data:\n%s\n", hex.Dump(pktGroup)) } - switch opcode { - case network.MSG_MHF_ENUMERATE_EVENT: - fallthrough - case network.MSG_MHF_ENUMERATE_QUEST: - fallthrough - case network.MSG_MHF_ENUMERATE_RANKING: - fallthrough - case network.MSG_MHF_READ_MERCENARY_W: - fallthrough - case network.MSG_MHF_GET_ETC_POINTS: - fallthrough - case network.MSG_MHF_READ_GUILDCARD: - fallthrough - case network.MSG_MHF_READ_BEAT_LEVEL: - fallthrough - case network.MSG_MHF_GET_EARTH_STATUS: - fallthrough - case network.MSG_MHF_GET_EARTH_VALUE: - fallthrough - case network.MSG_MHF_GET_WEEKLY_SCHEDULE: - fallthrough - case network.MSG_MHF_LIST_MEMBER: - fallthrough - case network.MSG_MHF_LOAD_PLATE_DATA: - fallthrough - case network.MSG_MHF_LOAD_PLATE_BOX: - fallthrough - case network.MSG_MHF_LOAD_FAVORITE_QUEST: - fallthrough - case network.MSG_MHF_LOAD_PARTNER: - fallthrough - case network.MSG_MHF_GET_TOWER_INFO: - fallthrough - case network.MSG_MHF_LOAD_OTOMO_AIROU: - fallthrough - case network.MSG_MHF_LOAD_DECO_MYSET: - fallthrough - case network.MSG_MHF_LOAD_HUNTER_NAVI: - fallthrough - case network.MSG_MHF_GET_UD_SCHEDULE: - fallthrough - case network.MSG_MHF_GET_UD_INFO: - fallthrough - case network.MSG_MHF_GET_UD_MONSTER_POINT: - fallthrough - case network.MSG_MHF_GET_RAND_FROM_TABLE: - fallthrough - case network.MSG_MHF_ACQUIRE_MONTHLY_REWARD: - fallthrough - case network.MSG_MHF_LOAD_PLATE_MYSET: - fallthrough - case network.MSG_MHF_LOAD_RENGOKU_DATA: - fallthrough - case network.MSG_MHF_ENUMERATE_SHOP: - fallthrough - case network.MSG_MHF_LOAD_SCENARIO_DATA: - fallthrough - case network.MSG_MHF_GET_BOOST_TIME_LIMIT: - fallthrough - case network.MSG_MHF_GET_BOOST_RIGHT: - fallthrough - case network.MSG_MHF_GET_REWARD_SONG: - fallthrough - case network.MSG_MHF_GET_GACHA_POINT: - fallthrough - case network.MSG_MHF_GET_KOURYOU_POINT: - fallthrough - case network.MSG_MHF_GET_ENHANCED_MINIDATA: - - ackHandle := bf.ReadUint32() - - data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp.bin", opcode.String())) - if err != nil { - panic(err) - } - - bfw := byteframe.NewByteFrame() - bfw.WriteUint16(uint16(network.MSG_SYS_ACK)) - bfw.WriteUint32(ackHandle) - bfw.WriteBytes(data) - s.cryptConn.SendPacket(bfw.Data()) - - case network.MSG_MHF_INFO_FESTA: - ackHandle := bf.ReadUint32() - _ = bf.ReadUint32() - - data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp.bin", opcode.String())) - if err != nil { - panic(err) - } - - bfw := byteframe.NewByteFrame() - bfw.WriteUint16(uint16(network.MSG_SYS_ACK)) - bfw.WriteUint32(ackHandle) - bfw.WriteBytes(data) - s.cryptConn.SendPacket(bfw.Data()) - - case network.MSG_MHF_LOADDATA: - ackHandle := bf.ReadUint32() - - data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp%d.bin", opcode.String(), loadDataCount)) - if err != nil { - panic(err) - } - - bfw := byteframe.NewByteFrame() - bfw.WriteUint16(uint16(network.MSG_SYS_ACK)) - bfw.WriteUint32(ackHandle) - bfw.WriteBytes(data) - s.cryptConn.SendPacket(bfw.Data()) - - loadDataCount++ - if loadDataCount > 1 { - loadDataCount = 0 - } - case network.MSG_MHF_GET_PAPER_DATA: - ackHandle := bf.ReadUint32() - - data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp%d.bin", opcode.String(), getPaperDataCount)) - if err != nil { - panic(err) - } - - bfw := byteframe.NewByteFrame() - bfw.WriteUint16(uint16(network.MSG_SYS_ACK)) - bfw.WriteUint32(ackHandle) - bfw.WriteBytes(data) - s.cryptConn.SendPacket(bfw.Data()) - - getPaperDataCount++ - if getPaperDataCount > 7 { - getPaperDataCount = 0 - } - default: - // Get the packet parser and handler for this opcode. - mhfPkt := mhfpacket.FromOpcode(opcode) - if mhfPkt == nil { - fmt.Println("Got opcode which we don't know how to parse, can't parse anymore for this group") - return - } - - // Parse and handle the packet - mhfPkt.Parse(bf) - handlerTable[opcode](s, mhfPkt) - break + // Get the packet parser and handler for this opcode. + mhfPkt := mhfpacket.FromOpcode(opcode) + if mhfPkt == nil { + fmt.Println("Got opcode which we don't know how to parse, can't parse anymore for this group") + return } + // Parse and handle the packet + mhfPkt.Parse(bf) + handlerTable[opcode](s, mhfPkt) + // If there is more data on the stream that the .Parse method didn't read, then read another packet off it. remainingData := bf.DataFromCurrent() - if len(remainingData) >= 2 && (opcode == network.MSG_SYS_TIME || opcode == network.MSG_MHF_INFO_FESTA || opcode == network.MSG_SYS_EXTEND_THRESHOLD) { + if len(remainingData) >= 2 { s.handlePacketGroup(remainingData) } } - -/* -func handlePacket(cc *network.CryptConn, pkt []byte) { - defer func() { - if r := recover(); r != nil { - fmt.Println("Recovered from panic.") - } - }() - - bf := byteframe.NewByteFrameFromBytes(pkt) - opcode := network.PacketID(bf.ReadUint16()) - - if opcode == network.MSG_SYS_EXTEND_THRESHOLD { - opcode = network.PacketID(bf.ReadUint16()) - } - - fmt.Printf("Opcode: %s\n", opcode) - switch opcode { - case network.MSG_SYS_PING: - ackHandle := bf.ReadUint32() - _ = bf.ReadUint16() - - bfw := byteframe.NewByteFrame() - bfw.WriteUint16(uint16(network.MSG_SYS_ACK)) - bfw.WriteUint32(ackHandle) - bfw.WriteUint32(0) - bfw.WriteUint32(0) - cc.SendPacket(bfw.Data()) - case network.MSG_SYS_TIME: - _ = bf.ReadUint8() - timestamp := bf.ReadUint32() // unix timestamp, e.g. 1577105879 - - bfw := byteframe.NewByteFrame() - bfw.WriteUint16(uint16(network.MSG_SYS_TIME)) - bfw.WriteUint8(0) - bfw.WriteUint32(timestamp) - cc.SendPacket(bfw.Data()) - case network.MSG_SYS_LOGIN: - ackHandle := bf.ReadUint32() - charID0 := bf.ReadUint32() - loginTokenNumber := bf.ReadUint32() - hardcodedZero0 := bf.ReadUint16() - requestVersion := bf.ReadUint16() - charID1 := bf.ReadUint32() - hardcodedZero1 := bf.ReadUint16() - loginTokenLength := bf.ReadUint16() // hardcoded to 0x11 - loginTokenString := bf.ReadBytes(17) - - _ = ackHandle - _ = charID0 - _ = loginTokenNumber - _ = hardcodedZero0 - _ = requestVersion - _ = charID1 - _ = hardcodedZero1 - _ = loginTokenLength - _ = loginTokenString - - bfw := byteframe.NewByteFrame() - bfw.WriteUint16(uint16(network.MSG_SYS_ACK)) - bfw.WriteUint32(ackHandle) - bfw.WriteUint64(0x000000005E00B9C2) // Timestamp? - cc.SendPacket(bfw.Data()) - - case network.MSG_MHF_ENUMERATE_EVENT: - fallthrough - case network.MSG_MHF_ENUMERATE_QUEST: - fallthrough - case network.MSG_MHF_ENUMERATE_RANKING: - fallthrough - case network.MSG_MHF_READ_MERCENARY_W: - fallthrough - case network.MSG_MHF_GET_ETC_POINTS: - fallthrough - case network.MSG_MHF_READ_GUILDCARD: - fallthrough - case network.MSG_MHF_READ_BEAT_LEVEL: - fallthrough - case network.MSG_MHF_GET_EARTH_STATUS: - fallthrough - case network.MSG_MHF_GET_EARTH_VALUE: - fallthrough - case network.MSG_MHF_GET_WEEKLY_SCHEDULE: - fallthrough - case network.MSG_MHF_LIST_MEMBER: - fallthrough - case network.MSG_MHF_LOAD_PLATE_DATA: - fallthrough - case network.MSG_MHF_LOAD_PLATE_BOX: - fallthrough - case network.MSG_MHF_LOAD_FAVORITE_QUEST: - fallthrough - case network.MSG_MHF_LOAD_PARTNER: - fallthrough - case network.MSG_MHF_GET_TOWER_INFO: - fallthrough - case network.MSG_MHF_LOAD_OTOMO_AIROU: - fallthrough - case network.MSG_MHF_LOAD_DECO_MYSET: - fallthrough - case network.MSG_MHF_LOAD_HUNTER_NAVI: - fallthrough - case network.MSG_MHF_GET_UD_SCHEDULE: - fallthrough - case network.MSG_MHF_GET_UD_INFO: - fallthrough - case network.MSG_MHF_GET_UD_MONSTER_POINT: - fallthrough - case network.MSG_MHF_GET_RAND_FROM_TABLE: - fallthrough - case network.MSG_MHF_ACQUIRE_MONTHLY_REWARD: - fallthrough - case network.MSG_MHF_GET_RENGOKU_RANKING_RANK: - fallthrough - case network.MSG_MHF_LOAD_PLATE_MYSET: - fallthrough - case network.MSG_MHF_LOAD_RENGOKU_DATA: - fallthrough - case network.MSG_MHF_ENUMERATE_SHOP: - fallthrough - case network.MSG_MHF_LOAD_SCENARIO_DATA: - fallthrough - case network.MSG_MHF_GET_BOOST_TIME_LIMIT: - fallthrough - case network.MSG_MHF_GET_BOOST_RIGHT: - fallthrough - case network.MSG_MHF_GET_REWARD_SONG: - fallthrough - case network.MSG_MHF_GET_GACHA_POINT: - fallthrough - case network.MSG_MHF_GET_KOURYOU_POINT: - fallthrough - case network.MSG_MHF_GET_ENHANCED_MINIDATA: - - ackHandle := bf.ReadUint32() - - data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp.bin", opcode.String())) - if err != nil { - panic(err) - } - - bfw := byteframe.NewByteFrame() - bfw.WriteUint16(uint16(network.MSG_SYS_ACK)) - bfw.WriteUint32(ackHandle) - bfw.WriteBytes(data) - cc.SendPacket(bfw.Data()) - - case network.MSG_MHF_INFO_FESTA: - ackHandle := bf.ReadUint32() - _ = bf.ReadUint32() - - data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp.bin", opcode.String())) - if err != nil { - panic(err) - } - - bfw := byteframe.NewByteFrame() - bfw.WriteUint16(uint16(network.MSG_SYS_ACK)) - bfw.WriteUint32(ackHandle) - bfw.WriteBytes(data) - cc.SendPacket(bfw.Data()) - - case network.MSG_MHF_LOADDATA: - ackHandle := bf.ReadUint32() - - data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp%d.bin", opcode.String(), loadDataCount)) - if err != nil { - panic(err) - } - - bfw := byteframe.NewByteFrame() - bfw.WriteUint16(uint16(network.MSG_SYS_ACK)) - bfw.WriteUint32(ackHandle) - bfw.WriteBytes(data) - cc.SendPacket(bfw.Data()) - - loadDataCount++ - if loadDataCount > 1 { - loadDataCount = 0 - } - case network.MSG_MHF_GET_PAPER_DATA: - ackHandle := bf.ReadUint32() - - data, err := ioutil.ReadFile(fmt.Sprintf("bin_resp/%s_resp%d.bin", opcode.String(), getPaperDataCount)) - if err != nil { - panic(err) - } - - bfw := byteframe.NewByteFrame() - bfw.WriteUint16(uint16(network.MSG_SYS_ACK)) - bfw.WriteUint32(ackHandle) - bfw.WriteBytes(data) - cc.SendPacket(bfw.Data()) - - getPaperDataCount++ - if getPaperDataCount > 7 { - getPaperDataCount = 0 - } - default: - fmt.Printf("Data:\n%s\n", hex.Dump(pkt)) - break - } - - remainingData := bf.DataFromCurrent() - if len(remainingData) >= 2 && (opcode == network.MSG_SYS_TIME || opcode == network.MSG_MHF_INFO_FESTA) { - handlePacket(cc, remainingData) - } -} -*/ diff --git a/server/entranceserver/entrance_server.go b/server/entranceserver/entrance_server.go index 8ac3a9846..3760def97 100644 --- a/server/entranceserver/entrance_server.go +++ b/server/entranceserver/entrance_server.go @@ -1,7 +1,6 @@ package entranceserver import ( - "database/sql" "encoding/hex" "fmt" "io" @@ -10,6 +9,7 @@ import ( "github.com/Andoryuuta/Erupe/config" "github.com/Andoryuuta/Erupe/network" + "github.com/jmoiron/sqlx" "go.uber.org/zap" ) @@ -18,7 +18,7 @@ type Server struct { sync.Mutex logger *zap.Logger erupeConfig *config.Config - db *sql.DB + db *sqlx.DB listener net.Listener isShuttingDown bool } @@ -26,7 +26,7 @@ type Server struct { // Config struct allows configuring the server. type Config struct { Logger *zap.Logger - DB *sql.DB + DB *sqlx.DB ErupeConfig *config.Config } diff --git a/server/entranceserver/make_resp.go b/server/entranceserver/make_resp.go index 73ac12ea6..0a2ab71c2 100644 --- a/server/entranceserver/make_resp.go +++ b/server/entranceserver/make_resp.go @@ -2,6 +2,9 @@ package entranceserver import ( "encoding/binary" + "fmt" + "io/ioutil" + "log" "net" "github.com/Andoryuuta/Erupe/config" @@ -32,6 +35,7 @@ func encodeServerInfo(serverInfos []config.EntranceServerInfo) []byte { bf.WriteUint32(si.AllowedClientFlags) for channelIdx, ci := range si.Channels { + fmt.Println("Channel idx", channelIdx) bf.WriteUint16(ci.Port) bf.WriteUint16(16 + uint16(channelIdx)) bf.WriteUint16(ci.MaxPlayers) @@ -71,6 +75,7 @@ func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byt } func makeResp(servers []config.EntranceServerInfo) []byte { + fmt.Printf("%+v\n", servers) rawServerData := encodeServerInfo(servers) bf := byteframe.NewByteFrame() @@ -81,6 +86,11 @@ func makeResp(servers []config.EntranceServerInfo) []byte { // If so, how does it work without the entrance server connection being authenticated? bf.WriteBytes(makeHeader([]byte{}, "USR", 0, 0x00)) + err := ioutil.WriteFile("go_entrance_resp.bin", bf.Data(), 0644) + if err != nil { + log.Fatal(err) + } + return bf.Data() } diff --git a/server/launcherserver/launcher_server.go b/server/launcherserver/launcher_server.go index 8d2e39adb..a3297ebe9 100644 --- a/server/launcherserver/launcher_server.go +++ b/server/launcherserver/launcher_server.go @@ -2,7 +2,6 @@ package launcherserver import ( "context" - "database/sql" "fmt" "net/http" "os" @@ -12,13 +11,14 @@ import ( "github.com/Andoryuuta/Erupe/config" "github.com/gorilla/handlers" "github.com/gorilla/mux" + "github.com/jmoiron/sqlx" "go.uber.org/zap" ) // Config struct allows configuring the server. type Config struct { Logger *zap.Logger - DB *sql.DB + DB *sqlx.DB ErupeConfig *config.Config UseOriginalLauncherFiles bool } @@ -28,7 +28,7 @@ type Server struct { sync.Mutex logger *zap.Logger erupeConfig *config.Config - db *sql.DB + db *sqlx.DB httpServer *http.Server useOriginalLauncherFiles bool isShuttingDown bool diff --git a/server/launcherserver/routes.go b/server/launcherserver/routes.go index bcdce830b..c951bf08a 100644 --- a/server/launcherserver/routes.go +++ b/server/launcherserver/routes.go @@ -13,7 +13,7 @@ func serverList(s *Server, w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, ``, s.erupeConfig.HostIP, - s.erupeConfig.Entrance.Port, + s.erupeConfig.Sign.Port, ) } diff --git a/server/signserver/dbutils.go b/server/signserver/dbutils.go new file mode 100644 index 000000000..bb5d4812d --- /dev/null +++ b/server/signserver/dbutils.go @@ -0,0 +1,56 @@ +package signserver + +import "time" + +func (s *Server) registerDBAccount(username string, password string) error { + _, err := s.db.Exec("INSERT INTO users (username, password) VALUES ($1, $2)", username, password) + if err != nil { + return err + } + + var id int + err = s.db.QueryRow("SELECT id FROM users WHERE username = $1", username).Scan(&id) + if err != nil { + return err + } + + // Create a base new character. + _, err = s.db.Exec(` + INSERT INTO characters ( + user_id, is_female, is_new_character, small_gr_level, gr_override_mode, name, unk_desc_string, + gr_override_level, gr_override_unk0, gr_override_unk1, exp, weapon, last_login) + VALUES($1, False, True, 0, True, '', '', 0, 0, 0, 0, 0, $2)`, + id, + uint32(time.Now().Unix()), + ) + if err != nil { + return err + } + + return nil +} + +type character struct { + ID uint32 `db:"id"` + IsFemale bool `db:"is_female"` + IsNewCharacter bool `db:"is_new_character"` + SmallGRLevel uint8 `db:"small_gr_level"` + GROverrideMode bool `db:"gr_override_mode"` + Name string `db:"name"` + UnkDescString string `db:"unk_desc_string"` + GROverrideLevel uint16 `db:"gr_override_level"` + GROverrideUnk0 uint8 `db:"gr_override_unk0"` + GROverrideUnk1 uint8 `db:"gr_override_unk1"` + Exp uint16 `db:"exp"` + Weapon uint16 `db:"weapon"` + LastLogin uint32 `db:"last_login"` +} + +func (s *Server) getCharactersForUser(uid int) ([]character, error) { + characters := []character{} + err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, small_gr_level, gr_override_mode, name, unk_desc_string, gr_override_level, gr_override_unk0, gr_override_unk1, exp, weapon, last_login FROM characters WHERE user_id = $1", uid) + if err != nil { + return nil, err + } + return characters, nil +} diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index 593368eca..c05737ab5 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -1,6 +1,11 @@ package signserver -import "github.com/Andoryuuta/byteframe" +import ( + "fmt" + + "github.com/Andoryuuta/byteframe" + "go.uber.org/zap" +) func paddedString(x string, size uint) []byte { out := make([]byte, size) @@ -27,91 +32,45 @@ func makeSignInFailureResp(respID RespID) []byte { return bf.Data() } -func (session *Session) makeSignInResp(username string) []byte { - bf := byteframe.NewByteFrame() +func (s *Session) makeSignInResp(uid int) []byte { + // Get the characters from the DB. + chars, err := s.server.getCharactersForUser(uid) + if err != nil { + s.logger.Warn("Error getting characters from DB", zap.Error(err)) + } - // delete me: - //bf.WriteUint8(8) - //return bf.Data() + bf := byteframe.NewByteFrame() bf.WriteUint8(1) // resp_code bf.WriteUint8(0) // file/patch server count bf.WriteUint8(4) // entrance server count - bf.WriteUint8(1) // character count + bf.WriteUint8(uint8(len(chars))) // character count bf.WriteUint32(0xFFFFFFFF) // login_token_number bf.WriteBytes(paddedString("logintokenstrng", 16)) // login_token (16 byte padded string) bf.WriteUint32(1576761190) - - // file patch server PascalStrings here - - // Array(this.entrance_server_count, PascalString(Byte, "utf8")), - uint8PascalString(bf, "localhost:53310") + uint8PascalString(bf, fmt.Sprintf("%s:%d", s.server.erupeConfig.HostIP, s.server.erupeConfig.Entrance.Port)) uint8PascalString(bf, "") uint8PascalString(bf, "") uint8PascalString(bf, "mhf-n.capcom.com.tw") - /////////////////////////// - // Characters: - - /* - tab = '123456789ABCDEFGHJKLMNPQRTUVWXYZ' - def make_uid_str(cid): - out = '' - for i in range(6): - v = (cid>>5*i) - out += tab[v&0x1f] - return out - - def make_cid_int(uid): - v = 0 - for c in uid[::-1]: - idx = tab.find(c) - if idx == -1: - raise Exception("not in tab") - v |= idx - v = v<<5 - return v>>5 - */ - bf.WriteUint32(469153291) // character ID 469153291 - bf.WriteUint16(999) // Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999 - - //44.204 - - /* - 0=大劍/Big sword - 1=重弩/Heavy crossbow - 2=大錘/Sledgehammer - 3=長槍/Spear - 4=單手劍/One-handed sword - 5=輕弩/Light crossbow - 6=雙劍/Double sword - 7=太刀/Tadao - 8=狩獵笛/Hunting flute - 9=銃槍/Shotgun - 10=弓/bow - 11=穿龍棍/Wear a dragon stick - 12=斬擊斧F/Chopping Axe F - 13=--- - default=不明/unknown - */ - bf.WriteUint16(7) // Weapon, 0-13. - - bf.WriteUint32(1576761172) // Last login date, unix timestamp in seconds. - bf.WriteUint8(1) // Sex, 0=male, 1=female. - bf.WriteUint8(0) // Is new character, 1 replaces character name with ?????. - grMode := uint8(0) - bf.WriteUint8(1) // GR level if grMode == 0 - bf.WriteUint8(grMode) // GR mode. - bf.WriteBytes(paddedString(username, 16)) // Character name - bf.WriteBytes(paddedString("0", 32)) // unk str - if grMode == 1 { - bf.WriteUint16(55) // GR level override. - bf.WriteUint8(0) // unk - bf.WriteUint8(0) // unk + for _, char := range chars { + bf.WriteUint32(char.ID) // character ID 469153291 + bf.WriteUint16(char.Exp) // Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999 + bf.WriteUint16(char.Weapon) // Weapon, 0-13. + bf.WriteUint32(char.LastLogin) // Last login date, unix timestamp in seconds. + bf.WriteBool(char.IsFemale) // Sex, 0=male, 1=female. + bf.WriteBool(char.IsNewCharacter) // Is new character, 1 replaces character name with ?????. + bf.WriteUint8(char.SmallGRLevel) // GR level if grMode == 0 + bf.WriteBool(char.GROverrideMode) // GR mode. + bf.WriteBytes(paddedString(char.Name, 16)) // Character name + bf.WriteBytes(paddedString(char.UnkDescString, 32)) // unk str + if char.GROverrideMode { + bf.WriteUint16(char.GROverrideLevel) // GR level override. + bf.WriteUint8(char.GROverrideUnk0) // unk + bf.WriteUint8(char.GROverrideUnk1) // unk + } } - ////////////////////////// - bf.WriteUint8(0) // friends_list_count bf.WriteUint8(0) // guild_members_count bf.WriteUint8(0) // notice_count diff --git a/server/signserver/session.go b/server/signserver/session.go index c749a79d1..7c2051a4e 100644 --- a/server/signserver/session.go +++ b/server/signserver/session.go @@ -95,6 +95,25 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error { case err == sql.ErrNoRows: s.logger.Info("Account not found", zap.String("reqUsername", reqUsername)) serverRespBytes = makeSignInFailureResp(SIGN_EAUTH) + + // HACK(Andoryuuta): Create a new account if it doesn't exit. + s.logger.Info("Creating account", zap.String("reqUsername", reqUsername), zap.String("reqPassword", reqPassword)) + err = s.server.registerDBAccount(reqUsername, reqPassword) + if err != nil { + s.logger.Info("Error on creating new account", zap.Error(err)) + serverRespBytes = makeSignInFailureResp(SIGN_EABORT) + break + } + + var id int + err = s.server.db.QueryRow("SELECT id FROM users WHERE username = $1", reqUsername).Scan(&id) + if err != nil { + s.logger.Info("Error on querying account id", zap.Error(err)) + serverRespBytes = makeSignInFailureResp(SIGN_EABORT) + break + } + + serverRespBytes = s.makeSignInResp(id) break case err != nil: serverRespBytes = makeSignInFailureResp(SIGN_EABORT) @@ -103,7 +122,7 @@ func (s *Session) handleDSGNRequest(bf *byteframe.ByteFrame) error { default: if reqPassword == password { s.logger.Info("Passwords match!") - serverRespBytes = s.makeSignInResp(reqUsername) + serverRespBytes = s.makeSignInResp(id) } else { s.logger.Info("Passwords don't match!") serverRespBytes = makeSignInFailureResp(SIGN_EPASS) diff --git a/server/signserver/sign_server.go b/server/signserver/sign_server.go index de6b60d5d..7a37189c0 100644 --- a/server/signserver/sign_server.go +++ b/server/signserver/sign_server.go @@ -1,7 +1,6 @@ package signserver import ( - "database/sql" "fmt" "io" "net" @@ -9,13 +8,14 @@ import ( "github.com/Andoryuuta/Erupe/config" "github.com/Andoryuuta/Erupe/network" + "github.com/jmoiron/sqlx" "go.uber.org/zap" ) // Config struct allows configuring the server. type Config struct { Logger *zap.Logger - DB *sql.DB + DB *sqlx.DB ErupeConfig *config.Config } @@ -26,7 +26,7 @@ type Server struct { erupeConfig *config.Config sid int sessions map[int]*Session - db *sql.DB + db *sqlx.DB listener net.Listener isShuttingDown bool } From d0234c85ab1953b2e6cd947bc5694fab523677c0 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Tue, 21 Jan 2020 18:10:37 -0500 Subject: [PATCH 06/49] Add MsgMhfGetUdTacticsFollower stub --- network/mhfpacket/msg_mhf_get_ud_tactics_follower.go | 7 +++++-- server/channelserver/handlers.go | 9 +++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/network/mhfpacket/msg_mhf_get_ud_tactics_follower.go b/network/mhfpacket/msg_mhf_get_ud_tactics_follower.go index 4c6231710..be80d9123 100644 --- a/network/mhfpacket/msg_mhf_get_ud_tactics_follower.go +++ b/network/mhfpacket/msg_mhf_get_ud_tactics_follower.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetUdTacticsFollower represents the MSG_MHF_GET_UD_TACTICS_FOLLOWER -type MsgMhfGetUdTacticsFollower struct{} +type MsgMhfGetUdTacticsFollower struct{ + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetUdTacticsFollower) Opcode() network.PacketID { @@ -15,7 +17,8 @@ func (m *MsgMhfGetUdTacticsFollower) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetUdTacticsFollower) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 8cb2f427f..1f9e09ec4 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -372,7 +372,9 @@ func handleMsgSysAuthTerminal(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysReserve5C(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysRightsReload(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysRightsReload(s *Session, p mhfpacket.MHFPacket) { + +} func handleMsgSysReserve5E(s *Session, p mhfpacket.MHFPacket) {} @@ -1432,7 +1434,10 @@ func handleMsgMhfGetEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUpdateEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdTacticsFollowe) + doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfSetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) {} From 4c839440cd0b2b58299a055473e0e0d86febfabe Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Tue, 21 Jan 2020 18:12:34 -0500 Subject: [PATCH 07/49] Fix typo --- server/channelserver/handlers.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 1f9e09ec4..4455367b9 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -152,9 +152,9 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { fmt.Println("Got chat message!") resp := &mhfpacket.MsgSysCastedBinary{ - CharID: s.charID, - Type0: 1, - Type1: 1, + CharID: s.charID, + Type0: 1, + Type1: 1, RawDataPayload: pkt.RawDataPayload, } s.server.BroadcastMHF(resp, s) @@ -1435,7 +1435,7 @@ func handleMsgMhfGetEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUpdateEquipSkinHist(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdTacticsFollower(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgMhfGetUdTacticsFollowe) + pkt := p.(*mhfpacket.MsgMhfGetUdTacticsFollower) doSizedAckResp(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) } From 76fe663ee1c71f95328a0e6b0955d9053fc3381c Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Tue, 21 Jan 2020 20:01:32 -0500 Subject: [PATCH 08/49] Add MsgSysReserve203/IsUpdateGuildMsgBoard --- network/mhfpacket/msg_sys_reserve203.go | 15 ++++-- server/channelserver/handlers.go | 69 +++++++++++++++---------- 2 files changed, 55 insertions(+), 29 deletions(-) diff --git a/network/mhfpacket/msg_sys_reserve203.go b/network/mhfpacket/msg_sys_reserve203.go index 5059a54f2..62f9e528c 100644 --- a/network/mhfpacket/msg_sys_reserve203.go +++ b/network/mhfpacket/msg_sys_reserve203.go @@ -5,8 +5,14 @@ import ( "github.com/Andoryuuta/byteframe" ) +// TODO(Andoryuuta): Make up a name for this packet, not reserved anymore. Called "Is_update_guild_msg_board" + // MsgSysReserve203 represents the MSG_SYS_reserve203 -type MsgSysReserve203 struct{} +type MsgSysReserve203 struct { + AckHandle uint32 + Unk0 uint16 // Hardcoded 0x0000 in the binary + Unk1 uint16 // Hardcoded 0x0500 in the binary. +} // Opcode returns the ID associated with this packet type. func (m *MsgSysReserve203) Opcode() network.PacketID { @@ -15,10 +21,13 @@ func (m *MsgSysReserve203) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysReserve203) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. func (m *MsgSysReserve203) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 4455367b9..88ed4d6ce 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -46,6 +46,33 @@ func doSizedAckResp(s *Session, ackHandle uint32, data []byte) { s.QueueAck(ackHandle, bfw.Data()) } +func updateRights(s *Session) { + update := &mhfpacket.MsgSysUpdateRight{ + Unk0: 0, + Unk1: 0x4E, + Rights: []mhfpacket.ClientRight{ + { + ID: 1, + Timestamp: 0, + }, + { + ID: 2, + Timestamp: 0x5dfa14c0, + }, + { + ID: 3, + Timestamp: 0x5dfa14c0, + }, + { + ID: 6, + Timestamp: 0x5de70510, + }, + }, + UnkSize: 0, + } + s.QueueSendMHF(update) +} + func fixedSizeShiftJIS(text string, size int) []byte { r := bytes.NewBuffer([]byte(text)) encoded, err := ioutil.ReadAll(transform.NewReader(r, japanese.ShiftJIS.NewEncoder())) @@ -522,6 +549,9 @@ func handleMsgMhfEntryRookieGuild(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateQuest) stubEnumerateNoResults(s, pkt.AckHandle) + + // Update the client's rights as well: + updateRights(s) } func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) { @@ -537,6 +567,9 @@ func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateRanking) stubEnumerateNoResults(s, pkt.AckHandle) + + // Update the client's rights as well: + updateRights(s) } func handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) { @@ -1012,30 +1045,7 @@ func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) { doSizedAckResp(s, pkt.AckHandle, []byte{}) // Update the client's rights as well: - update := &mhfpacket.MsgSysUpdateRight{ - Unk0: 0, - Unk1: 0x4E, - Rights: []mhfpacket.ClientRight{ - { - ID: 1, - Timestamp: 0, - }, - { - ID: 2, - Timestamp: 0x5dfa14c0, - }, - { - ID: 3, - Timestamp: 0x5dfa14c0, - }, - { - ID: 6, - Timestamp: 0x5de70510, - }, - }, - UnkSize: 0, - } - s.QueueSendMHF(update) + updateRights(s) } func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {} @@ -1608,9 +1618,16 @@ func handleMsgMhfUpdateForceGuildRank(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysReserve202(s *Session, p mhfpacket.MHFPacket) {} +// "Enumrate_guild_msg_board" +func handleMsgSysReserve202(s *Session, p mhfpacket.MHFPacket) { +} -func handleMsgSysReserve203(s *Session, p mhfpacket.MHFPacket) {} +// "Is_update_guild_msg_board" +func handleMsgSysReserve203(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysReserve203) + resp := make([]byte, 8) // Unk resp. + s.QueueAck(pkt.AckHandle, resp) +} func handleMsgSysReserve204(s *Session, p mhfpacket.MHFPacket) {} From b898614ddc985c34f66368a3ac32a96e1284cdea Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Tue, 21 Jan 2020 20:21:12 -0500 Subject: [PATCH 09/49] Add MsgMhfCheckWeeklyStamp --- network/mhfpacket/msg_mhf_check_weekly_stamp.go | 15 ++++++++++++--- server/channelserver/handlers.go | 15 ++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/network/mhfpacket/msg_mhf_check_weekly_stamp.go b/network/mhfpacket/msg_mhf_check_weekly_stamp.go index d5f725a87..b748c74d7 100644 --- a/network/mhfpacket/msg_mhf_check_weekly_stamp.go +++ b/network/mhfpacket/msg_mhf_check_weekly_stamp.go @@ -6,7 +6,12 @@ import ( ) // MsgMhfCheckWeeklyStamp represents the MSG_MHF_CHECK_WEEKLY_STAMP -type MsgMhfCheckWeeklyStamp struct{} +type MsgMhfCheckWeeklyStamp struct { + AckHandle uint32 + Unk0 uint8 + Unk1 bool + Unk2 uint16 // Hardcoded 0 in the binary +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfCheckWeeklyStamp) Opcode() network.PacketID { @@ -15,10 +20,14 @@ func (m *MsgMhfCheckWeeklyStamp) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfCheckWeeklyStamp) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadBool() + m.Unk2 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfCheckWeeklyStamp) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 88ed4d6ce..7c33400cd 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -651,7 +651,20 @@ func handleMsgMhfCheckMonthlyItem(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfAcquireMonthlyItem(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp) + + resp := byteframe.NewByteFrame() + resp.WriteUint16(0x0100) + resp.WriteUint16(0x000E) + resp.WriteUint16(0x0001) + resp.WriteUint16(0x0000) + resp.WriteUint16(0x0001) + resp.WriteUint32(0) + resp.WriteUint32(0x5dddcbb3) // Timestamp + + s.QueueAck(pkt.AckHandle, resp.Data()) +} func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {} From fa245e53fbd8ccc14828ef7f414a6b276cd54849 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Wed, 22 Jan 2020 17:53:27 -0500 Subject: [PATCH 10/49] Implement MsgSysEnumerateStage w/ stage map --- network/mhfpacket/msg_sys_enumerate_stage.go | 15 ++++++-- server/channelserver/channel_server.go | 9 ++++- server/channelserver/handlers.go | 39 +++++++++++++++++--- server/channelserver/stage.go | 9 +++++ 4 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 server/channelserver/stage.go diff --git a/network/mhfpacket/msg_sys_enumerate_stage.go b/network/mhfpacket/msg_sys_enumerate_stage.go index 65d60c02f..0c3362562 100644 --- a/network/mhfpacket/msg_sys_enumerate_stage.go +++ b/network/mhfpacket/msg_sys_enumerate_stage.go @@ -6,7 +6,12 @@ import ( ) // MsgSysEnumerateStage represents the MSG_SYS_ENUMERATE_STAGE -type MsgSysEnumerateStage struct{} +type MsgSysEnumerateStage struct { + AckHandle uint32 + Unk0 uint8 // Hardcoded 1 in the binary + StageIDLength uint8 + StageID string +} // Opcode returns the ID associated with this packet type. func (m *MsgSysEnumerateStage) Opcode() network.PacketID { @@ -15,10 +20,14 @@ func (m *MsgSysEnumerateStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysEnumerateStage) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.StageIDLength = bf.ReadUint8() + m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysEnumerateStage) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/server/channelserver/channel_server.go b/server/channelserver/channel_server.go index 0d11080c8..2a36a06b7 100644 --- a/server/channelserver/channel_server.go +++ b/server/channelserver/channel_server.go @@ -32,8 +32,8 @@ type Server struct { isShuttingDown bool - gameObjectLock sync.Mutex - gameObjectCount uint32 + stagesLock sync.RWMutex + stages map[string]*Stage } // NewServer creates a new Server type. @@ -45,7 +45,12 @@ func NewServer(config *Config) *Server { acceptConns: make(chan net.Conn), deleteConns: make(chan net.Conn), sessions: make(map[net.Conn]*Session), + stages: make(map[string]*Stage), } + + // Default town stage that clients try to enter without creating. + s.stages["sl1Ns200p0a0u0"] = &Stage{} + return s } diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 7c33400cd..21708db64 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -9,6 +9,7 @@ import ( "github.com/Andoryuuta/Erupe/network/mhfpacket" "github.com/Andoryuuta/byteframe" + "go.uber.org/zap" "golang.org/x/text/encoding/japanese" "golang.org/x/text/transform" ) @@ -285,7 +286,25 @@ func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysEnumerateStage) + + // Read-lock the stages. + s.server.stagesLock.RLock() + defer s.server.stagesLock.RUnlock() + + // Build the response + resp := byteframe.NewByteFrame() + resp.WriteUint16(uint16(len(s.server.stages))) + for sid := range s.server.stages { + // Couldn't find the parsing code in the client, unk purpose & sizes: + resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00}) + resp.WriteUint8(uint8(len(sid))) + resp.WriteBytes([]byte(sid)) + } + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgSysCreateMutex(s *Session, p mhfpacket.MHFPacket) {} @@ -322,11 +341,21 @@ func handleMsgSysNotifyRegister(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysCreateObject) + // Get the current stage. + s.server.stagesLock.RLock() + defer s.server.stagesLock.RUnlock() + stage, ok := s.server.stages[s.stageID] + if !ok { + s.logger.Fatal("StageID not in the stages map!", zap.String("stageID", s.stageID)) + } + + // Lock the stage. + stage.Lock() + defer stage.Unlock() + // Make a new object ID. - s.server.gameObjectLock.Lock() - objID := s.server.gameObjectCount - s.server.gameObjectCount++ - s.server.gameObjectLock.Unlock() + objID := stage.gameObjectCount + stage.gameObjectCount++ resp := byteframe.NewByteFrame() resp.WriteUint32(0) // Unk, is this echoed back from pkt.Unk0? diff --git a/server/channelserver/stage.go b/server/channelserver/stage.go new file mode 100644 index 000000000..2056d5af2 --- /dev/null +++ b/server/channelserver/stage.go @@ -0,0 +1,9 @@ +package channelserver + +import "sync" + +// Stage holds stage-specific information +type Stage struct { + sync.RWMutex + gameObjectCount uint32 +} From 5a67d689e047020ac32741b21fdcb1e0858200b1 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Wed, 22 Jan 2020 18:03:02 -0500 Subject: [PATCH 11/49] Fix MsgSysCreateObject stage lookup --- network/mhfpacket/msg_sys_enumerate_stage.go | 2 +- server/channelserver/handlers.go | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/network/mhfpacket/msg_sys_enumerate_stage.go b/network/mhfpacket/msg_sys_enumerate_stage.go index 0c3362562..79aff6016 100644 --- a/network/mhfpacket/msg_sys_enumerate_stage.go +++ b/network/mhfpacket/msg_sys_enumerate_stage.go @@ -10,7 +10,7 @@ type MsgSysEnumerateStage struct { AckHandle uint32 Unk0 uint8 // Hardcoded 1 in the binary StageIDLength uint8 - StageID string + StageID string // NULL terminated string. } // Opcode returns the ID associated with this packet type. diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 21708db64..21b4bd916 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "log" + "strings" "time" "github.com/Andoryuuta/Erupe/network/mhfpacket" @@ -89,6 +90,11 @@ func fixedSizeShiftJIS(text string, size int) []byte { return out } +// TODO(Andoryuuta): Fix/move/remove me! +func stripNullTerminator(x string) string { + return strings.SplitN(x, "\x00", 2)[0] +} + func handleMsgHead(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysReserve01(s *Session, p mhfpacket.MHFPacket) {} @@ -344,7 +350,7 @@ func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) { // Get the current stage. s.server.stagesLock.RLock() defer s.server.stagesLock.RUnlock() - stage, ok := s.server.stages[s.stageID] + stage, ok := s.server.stages[stripNullTerminator(s.stageID)] if !ok { s.logger.Fatal("StageID not in the stages map!", zap.String("stageID", s.stageID)) } From cf3cafa929415e7db94780615bbb9bc47f55935e Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Wed, 22 Jan 2020 18:17:23 -0500 Subject: [PATCH 12/49] Implement MsgSysCreateStage --- network/mhfpacket/msg_sys_create_stage.go | 17 ++++++++++++++--- server/channelserver/handlers.go | 11 ++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/network/mhfpacket/msg_sys_create_stage.go b/network/mhfpacket/msg_sys_create_stage.go index 642a3c23d..3eef61a52 100644 --- a/network/mhfpacket/msg_sys_create_stage.go +++ b/network/mhfpacket/msg_sys_create_stage.go @@ -6,7 +6,13 @@ import ( ) // MsgSysCreateStage represents the MSG_SYS_CREATE_STAGE -type MsgSysCreateStage struct{} +type MsgSysCreateStage struct { + AckHandle uint32 + Unk0 uint8 + Unk1 uint8 + StageIDLength uint8 + StageID string // NULL terminated string. +} // Opcode returns the ID associated with this packet type. func (m *MsgSysCreateStage) Opcode() network.PacketID { @@ -15,10 +21,15 @@ func (m *MsgSysCreateStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysCreateStage) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint8() + m.StageIDLength = bf.ReadUint8() + m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysCreateStage) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 21b4bd916..a6c9f8c53 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -252,7 +252,16 @@ func handleMsgSysRecordLog(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysEcho(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysCreateStage) + + s.server.stagesLock.Lock() + s.server.stages[stripNullTerminator(s.stageID)] = &Stage{} + s.server.stagesLock.Lock() + + resp := make([]byte, 8) // Unk resp. + s.QueueAck(pkt.AckHandle, resp) +} func handleMsgSysStageDestruct(s *Session, p mhfpacket.MHFPacket) {} From a92b5b0b937ec61909f5f12ae862148f8b15063a Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Wed, 22 Jan 2020 18:22:33 -0500 Subject: [PATCH 13/49] Fix typo deadlock --- server/channelserver/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index a6c9f8c53..74a7aa8ef 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -257,7 +257,7 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) { s.server.stagesLock.Lock() s.server.stages[stripNullTerminator(s.stageID)] = &Stage{} - s.server.stagesLock.Lock() + s.server.stagesLock.Unlock() resp := make([]byte, 8) // Unk resp. s.QueueAck(pkt.AckHandle, resp) From ea13520ca93ef5c89e151a163c393002b0db43c3 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 14:46:15 -0500 Subject: [PATCH 14/49] Implement MsgSysReserveStage --- network/mhfpacket/msg_sys_reserve_stage.go | 15 ++++++++++++--- server/channelserver/handlers.go | 8 +++++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/network/mhfpacket/msg_sys_reserve_stage.go b/network/mhfpacket/msg_sys_reserve_stage.go index daf097bd2..d6c65f7ea 100644 --- a/network/mhfpacket/msg_sys_reserve_stage.go +++ b/network/mhfpacket/msg_sys_reserve_stage.go @@ -6,7 +6,12 @@ import ( ) // MsgSysReserveStage represents the MSG_SYS_RESERVE_STAGE -type MsgSysReserveStage struct{} +type MsgSysReserveStage struct { + AckHandle uint32 + Unk0 uint8 // Made with: `16 * x | 1;`, unknown `x` values. + StageIDLength uint8 + StageID string // NULL terminated string. +} // Opcode returns the ID associated with this packet type. func (m *MsgSysReserveStage) Opcode() network.PacketID { @@ -15,10 +20,14 @@ func (m *MsgSysReserveStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysReserveStage) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.StageIDLength = bf.ReadUint8() + m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysReserveStage) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 74a7aa8ef..2b9126034 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -287,7 +287,13 @@ func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysReserveStage) + + fmt.Printf("Got reserve stage req, Unk0:%v, StageID:%q\n", pkt.Unk0, pkt.StageID) + + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {} From 865ba378ec457a688f934eabfa5483d2f156c049 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 15:53:45 -0500 Subject: [PATCH 15/49] Update MsgSysCreateStage fields --- network/mhfpacket/msg_sys_create_stage.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/network/mhfpacket/msg_sys_create_stage.go b/network/mhfpacket/msg_sys_create_stage.go index 3eef61a52..8fec57798 100644 --- a/network/mhfpacket/msg_sys_create_stage.go +++ b/network/mhfpacket/msg_sys_create_stage.go @@ -8,8 +8,8 @@ import ( // MsgSysCreateStage represents the MSG_SYS_CREATE_STAGE type MsgSysCreateStage struct { AckHandle uint32 - Unk0 uint8 - Unk1 uint8 + Unk0 uint8 // Likely only has 1 and 2 as values. + PlayerCount uint8 StageIDLength uint8 StageID string // NULL terminated string. } @@ -23,7 +23,7 @@ func (m *MsgSysCreateStage) Opcode() network.PacketID { func (m *MsgSysCreateStage) Parse(bf *byteframe.ByteFrame) error { m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint8() - m.Unk1 = bf.ReadUint8() + m.PlayerCount = bf.ReadUint8() m.StageIDLength = bf.ReadUint8() m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) return nil From cb275a7a187563488244427e7e5190afaeec25c8 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 15:59:30 -0500 Subject: [PATCH 16/49] Implement MsgSysSetStagePass parser --- network/mhfpacket/msg_sys_set_stage_pass.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/network/mhfpacket/msg_sys_set_stage_pass.go b/network/mhfpacket/msg_sys_set_stage_pass.go index f3de3f5f2..587801bb1 100644 --- a/network/mhfpacket/msg_sys_set_stage_pass.go +++ b/network/mhfpacket/msg_sys_set_stage_pass.go @@ -6,7 +6,11 @@ import ( ) // MsgSysSetStagePass represents the MSG_SYS_SET_STAGE_PASS -type MsgSysSetStagePass struct{} +type MsgSysSetStagePass struct { + Unk0 uint8 // Hardcoded 0 in the binary + PasswordLength uint8 + Password string // NULL-terminated string +} // Opcode returns the ID associated with this packet type. func (m *MsgSysSetStagePass) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgSysSetStagePass) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysSetStagePass) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.Unk0 = bf.ReadUint8() + m.PasswordLength = bf.ReadUint8() + m.Password = string(bf.ReadBytes(uint(m.PasswordLength))) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysSetStagePass) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} From 10c80322afc753ff7aa80061eb18a91a5b9023d3 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 16:49:00 -0500 Subject: [PATCH 17/49] Implement MsgSysSetStageBinary parser --- network/mhfpacket/msg_sys_set_stage_binary.go | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/network/mhfpacket/msg_sys_set_stage_binary.go b/network/mhfpacket/msg_sys_set_stage_binary.go index 5864cba6a..40d9e57bc 100644 --- a/network/mhfpacket/msg_sys_set_stage_binary.go +++ b/network/mhfpacket/msg_sys_set_stage_binary.go @@ -6,7 +6,14 @@ import ( ) // MsgSysSetStageBinary represents the MSG_SYS_SET_STAGE_BINARY -type MsgSysSetStageBinary struct{} +type MsgSysSetStageBinary struct { + Unk0 uint8 + BinaryType uint8 // Index + StageIDLength uint8 // <= 0x20 + DataSize uint16 // <= 0x400 + StageID string + RawDataPayload []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgSysSetStageBinary) Opcode() network.PacketID { @@ -15,10 +22,16 @@ func (m *MsgSysSetStageBinary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysSetStageBinary) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.Unk0 = bf.ReadUint8() + m.BinaryType = bf.ReadUint8() + m.StageIDLength = bf.ReadUint8() + m.DataSize = bf.ReadUint16() + m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) + m.RawDataPayload = bf.ReadBytes(uint(m.DataSize)) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysSetStageBinary) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} From 28e0dafd54c398ffa2126657f8957ba7834ec657 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 19:29:19 -0500 Subject: [PATCH 18/49] Stage/objects test --- network/mhfpacket/msg_sys_cleanup_object.go | 5 +- network/mhfpacket/msg_sys_duplicate_object.go | 17 ++- network/mhfpacket/msg_sys_enter_stage.go | 4 +- network/mhfpacket/msg_sys_enumerate_client.go | 17 ++- server/channelserver/channel_server.go | 3 +- server/channelserver/handlers.go | 106 +++++++++++++++--- server/channelserver/session.go | 1 + server/channelserver/stage.go | 49 +++++++- 8 files changed, 174 insertions(+), 28 deletions(-) diff --git a/network/mhfpacket/msg_sys_cleanup_object.go b/network/mhfpacket/msg_sys_cleanup_object.go index a5548cd4a..0f3881c8d 100644 --- a/network/mhfpacket/msg_sys_cleanup_object.go +++ b/network/mhfpacket/msg_sys_cleanup_object.go @@ -20,5 +20,6 @@ func (m *MsgSysCleanupObject) Parse(bf *byteframe.ByteFrame) error { // Build builds a binary packet from the current data. func (m *MsgSysCleanupObject) Build(bf *byteframe.ByteFrame) error { - panic("Not implemented") -} \ No newline at end of file + // This packet has no data. + return nil +} diff --git a/network/mhfpacket/msg_sys_duplicate_object.go b/network/mhfpacket/msg_sys_duplicate_object.go index 72105acad..3f5e43071 100644 --- a/network/mhfpacket/msg_sys_duplicate_object.go +++ b/network/mhfpacket/msg_sys_duplicate_object.go @@ -6,7 +6,12 @@ import ( ) // MsgSysDuplicateObject represents the MSG_SYS_DUPLICATE_OBJECT -type MsgSysDuplicateObject struct{} +type MsgSysDuplicateObject struct { + ObjID uint32 + X, Y, Z float32 + Unk0 uint32 + OwnerCharID uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysDuplicateObject) Opcode() network.PacketID { @@ -20,5 +25,11 @@ func (m *MsgSysDuplicateObject) Parse(bf *byteframe.ByteFrame) error { // Build builds a binary packet from the current data. func (m *MsgSysDuplicateObject) Build(bf *byteframe.ByteFrame) error { - panic("Not implemented") -} \ No newline at end of file + bf.WriteUint32(m.ObjID) + bf.WriteFloat32(m.X) + bf.WriteFloat32(m.Y) + bf.WriteFloat32(m.Z) + bf.WriteUint32(m.Unk0) + bf.WriteUint32(m.OwnerCharID) + return nil +} diff --git a/network/mhfpacket/msg_sys_enter_stage.go b/network/mhfpacket/msg_sys_enter_stage.go index ee8391988..addea7cfa 100644 --- a/network/mhfpacket/msg_sys_enter_stage.go +++ b/network/mhfpacket/msg_sys_enter_stage.go @@ -10,7 +10,7 @@ type MsgSysEnterStage struct { AckHandle uint32 UnkBool uint8 StageIDLength uint8 - StageID []byte + StageID string } // Opcode returns the ID associated with this packet type. @@ -23,7 +23,7 @@ func (m *MsgSysEnterStage) Parse(bf *byteframe.ByteFrame) error { m.AckHandle = bf.ReadUint32() m.UnkBool = bf.ReadUint8() m.StageIDLength = bf.ReadUint8() - m.StageID = bf.ReadBytes(uint(m.StageIDLength)) + m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) return nil } diff --git a/network/mhfpacket/msg_sys_enumerate_client.go b/network/mhfpacket/msg_sys_enumerate_client.go index 5d3bf1e00..17115db11 100644 --- a/network/mhfpacket/msg_sys_enumerate_client.go +++ b/network/mhfpacket/msg_sys_enumerate_client.go @@ -6,7 +6,13 @@ import ( ) // MsgSysEnumerateClient represents the MSG_SYS_ENUMERATE_CLIENT -type MsgSysEnumerateClient struct{} +type MsgSysEnumerateClient struct { + AckHandle uint32 + Unk0 uint8 // Hardcoded 1 in the client + Unk1 uint8 + StageIDLength uint8 + StageID string +} // Opcode returns the ID associated with this packet type. func (m *MsgSysEnumerateClient) Opcode() network.PacketID { @@ -15,10 +21,15 @@ func (m *MsgSysEnumerateClient) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysEnumerateClient) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint8() + m.StageIDLength = bf.ReadUint8() + m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysEnumerateClient) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/server/channelserver/channel_server.go b/server/channelserver/channel_server.go index 2a36a06b7..b2c2d9fdd 100644 --- a/server/channelserver/channel_server.go +++ b/server/channelserver/channel_server.go @@ -49,7 +49,8 @@ func NewServer(config *Config) *Server { } // Default town stage that clients try to enter without creating. - s.stages["sl1Ns200p0a0u0"] = &Stage{} + stage := NewStage("sl1Ns200p0a0u0") + s.stages[stage.id] = stage return s } diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 2b9126034..1212dff55 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -256,7 +256,8 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysCreateStage) s.server.stagesLock.Lock() - s.server.stages[stripNullTerminator(s.stageID)] = &Stage{} + stage := NewStage(stripNullTerminator(pkt.StageID)) + s.server.stages[stage.id] = stage s.server.stagesLock.Unlock() resp := make([]byte, 8) // Unk resp. @@ -268,13 +269,40 @@ func handleMsgSysStageDestruct(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysEnterStage) + // Remove this session from old stage clients list and put myself in the new one. + s.server.stagesLock.Lock() + newStage, gotNewStage := s.server.stages[stripNullTerminator(pkt.StageID)] + s.server.stagesLock.Unlock() + + // Remove from old stage. + if s.stage != nil { + s.stage.Lock() + delete(s.stage.clients, s) + s.stage.Unlock() + } + + // Add the new stage. + if gotNewStage { + newStage.Lock() + newStage.clients[s] = s.charID + newStage.Unlock() + } + + // Save our new stage ID and pointer to the new stage itself. s.Lock() - s.stageID = string(pkt.StageID) + s.stageID = string(stripNullTerminator(pkt.StageID)) + s.stage = newStage s.Unlock() - //TODO: Send MSG_SYS_CLEANUP_OBJECT here before the client changes stages. + // Tell the client to cleanup it's current stage objects. + s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{}) + // Confirm the stage entry. s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + + // TODO(Andoryuuta): Notify existing stage clients that this new client has entered. + // TODO(Andoryuuta): Notify this client about all of the existing clients in the stage. + } func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {} @@ -292,6 +320,8 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { fmt.Printf("Got reserve stage req, Unk0:%v, StageID:%q\n", pkt.Unk0, pkt.StageID) + // TODO(Andoryuuta): Add proper player-slot reservations for stages. + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) } @@ -305,7 +335,31 @@ func handleMsgSysSetStageBinary(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysEnumerateClient) + + // Read-lock the stages map. + s.server.stagesLock.RLock() + + stage, ok := s.server.stages[stripNullTerminator(pkt.StageID)] + if !ok { + s.logger.Fatal("Can't enumerate clients for stage that doesn't exist!", zap.String("stageID", pkt.StageID)) + } + + // Unlock the stages map. + s.server.stagesLock.RUnlock() + + // Read-lock the stage and make the response with all of the charID's in the stage. + resp := byteframe.NewByteFrame() + stage.RLock() + resp.WriteUint16(uint16(len(stage.clients))) // Client count + for session := range stage.clients { + resp.WriteUint32(session.charID) // Client represented by charID + } + stage.RUnlock() + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysEnumerateStage) @@ -362,27 +416,47 @@ func handleMsgSysNotifyRegister(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysCreateObject) - // Get the current stage. - s.server.stagesLock.RLock() - defer s.server.stagesLock.RUnlock() - stage, ok := s.server.stages[stripNullTerminator(s.stageID)] - if !ok { + // Make sure we have a stage. + if s.stage == nil { s.logger.Fatal("StageID not in the stages map!", zap.String("stageID", s.stageID)) } // Lock the stage. - stage.Lock() - defer stage.Unlock() + s.stage.Lock() - // Make a new object ID. - objID := stage.gameObjectCount - stage.gameObjectCount++ + // Make a new stage object and insert it into the stage. + objID := s.stage.gameObjectCount + s.stage.gameObjectCount++ + newObj := &StageObject{ + id: objID, + ownerCharID: s.charID, + x: pkt.X, + y: pkt.Y, + z: pkt.Z, + } + + s.stage.objects[objID] = newObj + + // Unlock the stage. + s.stage.Unlock() + + // Response to our requesting client. resp := byteframe.NewByteFrame() resp.WriteUint32(0) // Unk, is this echoed back from pkt.Unk0? resp.WriteUint32(objID) // New local obj handle. - s.QueueAck(pkt.AckHandle, resp.Data()) + + // Duplicate the object creation to all sessions in the same stage. + dupObjUpdate := &mhfpacket.MsgSysDuplicateObject{ + ObjID: objID, + X: pkt.X, + Y: pkt.Y, + Z: pkt.Z, + Unk0: 0, + OwnerCharID: s.charID, + } + s.stage.BroadcastMHF(dupObjUpdate, s) } func handleMsgSysDeleteObject(s *Session, p mhfpacket.MHFPacket) {} @@ -391,6 +465,8 @@ func handleMsgSysPositionObject(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysPositionObject) fmt.Printf("Moved object %v to (%f,%f,%f)\n", pkt.ObjID, pkt.X, pkt.Y, pkt.Z) + // One of the few packets we can just re-broadcast directly. + s.stage.BroadcastMHF(pkt, s) } func handleMsgSysRotateObject(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/session.go b/server/channelserver/session.go index 1024df74b..e41cb17dc 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -22,6 +22,7 @@ type Session struct { sendPackets chan []byte stageID string + stage *Stage charID uint32 } diff --git a/server/channelserver/stage.go b/server/channelserver/stage.go index 2056d5af2..33967cf66 100644 --- a/server/channelserver/stage.go +++ b/server/channelserver/stage.go @@ -1,9 +1,54 @@ package channelserver -import "sync" +import ( + "sync" + + "github.com/Andoryuuta/Erupe/network/mhfpacket" + "github.com/Andoryuuta/byteframe" +) + +// StageObject holds infomation about a specific stage object. +type StageObject struct { + sync.RWMutex + id uint32 + ownerCharID uint32 + x, y, z float32 +} // Stage holds stage-specific information type Stage struct { sync.RWMutex - gameObjectCount uint32 + id string // Stage ID string + gameObjectCount uint32 // Total count of objects ever created for this stage. Used for ObjID generation. + objects map[uint32]*StageObject // Map of ObjID -> StageObject + clients map[*Session]uint32 // Map of session -> charID +} + +// NewStage creates a new stage with intialized values. +func NewStage(ID string) *Stage { + s := &Stage{ + objects: make(map[uint32]*StageObject), + clients: make(map[*Session]uint32), + } + + return s +} + +// BroadcastMHF queues a MHFPacket to be sent to all sessions in the stage. +func (s *Stage) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) { + // Make the header + bf := byteframe.NewByteFrame() + bf.WriteUint16(uint16(pkt.Opcode())) + + // Build the packet onto the byteframe. + pkt.Build(bf) + + // Broadcast the data. + for session := range s.clients { + if session == ignoredSession { + continue + } + // Enqueue in a non-blocking way that drops the packet if the connections send buffer channel is full. + session.QueueSendNonBlocking(bf.Data()) + } } From b1b4f390f058be4f0b06a4087fb34a9b98600c00 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 19:59:26 -0500 Subject: [PATCH 19/49] Stage object notification test --- network/mhfpacket/msg_sys_insert_user.go | 9 ++++-- .../mhfpacket/msg_sys_notify_user_binary.go | 11 +++++-- server/channelserver/handlers.go | 29 ++++++++++++++++++- server/channelserver/session.go | 2 +- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/network/mhfpacket/msg_sys_insert_user.go b/network/mhfpacket/msg_sys_insert_user.go index b8aa206e6..13466c493 100644 --- a/network/mhfpacket/msg_sys_insert_user.go +++ b/network/mhfpacket/msg_sys_insert_user.go @@ -6,7 +6,9 @@ import ( ) // MsgSysInsertUser represents the MSG_SYS_INSERT_USER -type MsgSysInsertUser struct{} +type MsgSysInsertUser struct { + CharID uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysInsertUser) Opcode() network.PacketID { @@ -20,5 +22,6 @@ func (m *MsgSysInsertUser) Parse(bf *byteframe.ByteFrame) error { // Build builds a binary packet from the current data. func (m *MsgSysInsertUser) Build(bf *byteframe.ByteFrame) error { - panic("Not implemented") -} \ No newline at end of file + bf.WriteUint32(m.CharID) + return nil +} diff --git a/network/mhfpacket/msg_sys_notify_user_binary.go b/network/mhfpacket/msg_sys_notify_user_binary.go index e4914aeef..2fbb5a9cf 100644 --- a/network/mhfpacket/msg_sys_notify_user_binary.go +++ b/network/mhfpacket/msg_sys_notify_user_binary.go @@ -6,7 +6,10 @@ import ( ) // MsgSysNotifyUserBinary represents the MSG_SYS_NOTIFY_USER_BINARY -type MsgSysNotifyUserBinary struct{} +type MsgSysNotifyUserBinary struct { + CharID uint32 + BinaryType uint8 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysNotifyUserBinary) Opcode() network.PacketID { @@ -20,5 +23,7 @@ func (m *MsgSysNotifyUserBinary) Parse(bf *byteframe.ByteFrame) error { // Build builds a binary packet from the current data. func (m *MsgSysNotifyUserBinary) Build(bf *byteframe.ByteFrame) error { - panic("Not implemented") -} \ No newline at end of file + bf.WriteUint32(m.CharID) + bf.WriteUint8(m.BinaryType) + return nil +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 1212dff55..4e8f196e3 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -301,8 +301,35 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) // TODO(Andoryuuta): Notify existing stage clients that this new client has entered. - // TODO(Andoryuuta): Notify this client about all of the existing clients in the stage. + insertUserPkt := &mhfpacket.MsgSysInsertUser{ + CharID: s.charID, + } + s.stage.BroadcastMHF(insertUserPkt, s) + // Just the first user binary type (name) for right now. + notifyUserBinary1Pkt := &mhfpacket.MsgSysNotifyUserBinary{ + CharID: s.charID, + BinaryType: 1, + } + s.stage.BroadcastMHF(notifyUserBinary1Pkt, s) + + // TODO(Andoryuuta): Notify this client about all of the existing clients in the stage. + s.stage.RLock() + clientNotif := byteframe.NewByteFrame() + for session := range s.stage.clients { + (&mhfpacket.MsgSysInsertUser{ + CharID: session.charID, + }).Build(clientNotif) + + // Just the first user binary type (name) for right now. + (&mhfpacket.MsgSysNotifyUserBinary{ + CharID: session.charID, + BinaryType: 1, + }).Build(clientNotif) + } + s.stage.RUnlock() + + s.QueueSend(clientNotif.Data()) } func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/session.go b/server/channelserver/session.go index e41cb17dc..55afd0d0a 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -29,7 +29,7 @@ type Session struct { // NewSession creates a new Session type. func NewSession(server *Server, conn net.Conn) *Session { s := &Session{ - logger: server.logger, + logger: server.logger.Named(conn.RemoteAddr().String()), server: server, rawConn: conn, cryptConn: network.NewCryptConn(conn), From 1afe6860e25ebae48cf87dc7b2317d6feb8625e8 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 20:15:56 -0500 Subject: [PATCH 20/49] Print recovered panic error --- server/channelserver/session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/channelserver/session.go b/server/channelserver/session.go index 55afd0d0a..0a6b6c8ee 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -116,7 +116,7 @@ func (s *Session) handlePacketGroup(pktGroup []byte) { // This shouldn't be needed, but it's better to recover and let the connection die than to panic the server. defer func() { if r := recover(); r != nil { - fmt.Println("Recovered from panic.") + fmt.Println("Recovered from panic ", r) } }() From 0ac35f874303d96ab4ca33fb91404dde5b5ae5b3 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 20:22:42 -0500 Subject: [PATCH 21/49] Fix Stage init --- server/channelserver/stage.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/channelserver/stage.go b/server/channelserver/stage.go index 33967cf66..5bf38925d 100644 --- a/server/channelserver/stage.go +++ b/server/channelserver/stage.go @@ -27,6 +27,7 @@ type Stage struct { // NewStage creates a new stage with intialized values. func NewStage(ID string) *Stage { s := &Stage{ + id: ID, objects: make(map[uint32]*StageObject), clients: make(map[*Session]uint32), } From 3b443344a24d515ba218b6f18f4ed485e1c81e8a Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 20:29:56 -0500 Subject: [PATCH 22/49] Make object ID start at 1 --- server/channelserver/stage.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/server/channelserver/stage.go b/server/channelserver/stage.go index 5bf38925d..219749fab 100644 --- a/server/channelserver/stage.go +++ b/server/channelserver/stage.go @@ -27,9 +27,10 @@ type Stage struct { // NewStage creates a new stage with intialized values. func NewStage(ID string) *Stage { s := &Stage{ - id: ID, - objects: make(map[uint32]*StageObject), - clients: make(map[*Session]uint32), + id: ID, + objects: make(map[uint32]*StageObject), + clients: make(map[*Session]uint32), + gameObjectCount: 1, } return s From 86d4ec55cb64726a32b55252e807fa958cc80c77 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 20:32:15 -0500 Subject: [PATCH 23/49] Implement MsgSysPositionObject Build --- network/mhfpacket/msg_sys_position_object.go | 6 +++++- server/channelserver/handlers.go | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/network/mhfpacket/msg_sys_position_object.go b/network/mhfpacket/msg_sys_position_object.go index 7bb70157c..5ba3152df 100644 --- a/network/mhfpacket/msg_sys_position_object.go +++ b/network/mhfpacket/msg_sys_position_object.go @@ -27,5 +27,9 @@ func (m *MsgSysPositionObject) Parse(bf *byteframe.ByteFrame) error { // Build builds a binary packet from the current data. func (m *MsgSysPositionObject) Build(bf *byteframe.ByteFrame) error { - panic("Not implemented") + bf.WriteUint32(m.ObjID) + bf.WriteFloat32(m.X) + bf.WriteFloat32(m.Y) + bf.WriteFloat32(m.Z) + return nil } \ No newline at end of file diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 4e8f196e3..91a31c507 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -532,7 +532,9 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) { //pkt := p.(*mhfpacket.MsgSysSetUserBinary) } -func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) { + +} func handleMsgSysNotifyUserBinary(s *Session, p mhfpacket.MHFPacket) {} From 23838bdb7848845349c0c1bd550d6b5d346da24c Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 20:52:52 -0500 Subject: [PATCH 24/49] Add debug logging and stage entry fixes --- server/channelserver/handlers.go | 47 ++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 91a31c507..9bd0fe893 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -300,20 +300,31 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { // Confirm the stage entry. s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - // TODO(Andoryuuta): Notify existing stage clients that this new client has entered. - insertUserPkt := &mhfpacket.MsgSysInsertUser{ + // Notify existing stage clients that this new client has entered. + s.logger.Info("Sending MsgSysInsertUser & MsgSysNotifyUserBinary") + s.stage.BroadcastMHF(&mhfpacket.MsgSysInsertUser{ CharID: s.charID, - } - s.stage.BroadcastMHF(insertUserPkt, s) + }, s) - // Just the first user binary type (name) for right now. - notifyUserBinary1Pkt := &mhfpacket.MsgSysNotifyUserBinary{ + s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ CharID: s.charID, BinaryType: 1, - } - s.stage.BroadcastMHF(notifyUserBinary1Pkt, s) + }, s) + + // Just the first user binary type (name) for right now. + /* + s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ + CharID: s.charID, + BinaryType: 2, + }, s) + s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ + CharID: s.charID, + BinaryType: 3, + }, s) + */ // TODO(Andoryuuta): Notify this client about all of the existing clients in the stage. + s.logger.Info("Notifying entree about existing stage clients") s.stage.RLock() clientNotif := byteframe.NewByteFrame() for session := range s.stage.clients { @@ -328,8 +339,26 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { }).Build(clientNotif) } s.stage.RUnlock() - + clientNotif.WriteUint16(0x0010) // End it. s.QueueSend(clientNotif.Data()) + + // Notify the client to duplicate the existing objects. + s.logger.Info("Notifying entree about existing stage objects") + clientDupObjNotif := byteframe.NewByteFrame() + s.stage.RLock() + for _, obj := range s.stage.objects { + (&mhfpacket.MsgSysDuplicateObject{ + ObjID: obj.id, + X: obj.x, + Y: obj.y, + Z: obj.z, + Unk0: 0, + OwnerCharID: obj.ownerCharID, + }).Build(clientDupObjNotif) + } + s.stage.RUnlock() + clientDupObjNotif.WriteUint16(0x0010) // End it. + s.QueueSend(clientDupObjNotif.Data()) } func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {} From 1b74b6496e7f06884f3050e74e6996bfa1731ad9 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 21:06:25 -0500 Subject: [PATCH 25/49] Implement MsgSysGetUserBinary name stub --- network/mhfpacket/msg_sys_get_user_binary.go | 13 ++++++++++--- server/channelserver/handlers.go | 9 +++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/network/mhfpacket/msg_sys_get_user_binary.go b/network/mhfpacket/msg_sys_get_user_binary.go index 5db54bba4..27c279d3c 100644 --- a/network/mhfpacket/msg_sys_get_user_binary.go +++ b/network/mhfpacket/msg_sys_get_user_binary.go @@ -6,7 +6,11 @@ import ( ) // MsgSysGetUserBinary represents the MSG_SYS_GET_USER_BINARY -type MsgSysGetUserBinary struct{} +type MsgSysGetUserBinary struct { + AckHandle uint32 + CharID uint32 + BinaryType uint8 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysGetUserBinary) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgSysGetUserBinary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysGetUserBinary) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.CharID = bf.ReadUint32() + m.BinaryType = bf.ReadUint8() + return nil } // Build builds a binary packet from the current data. func (m *MsgSysGetUserBinary) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 9bd0fe893..a79cff94e 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -562,7 +562,16 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) { } func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysGetUserBinary) + + resp := bf.NewByteFrame() + if pkt.BinaryType == 1 { + // Stub name response with character ID + resp.WriteBytes([]byte(fmt.Sprintf("CID%d", s.charID))) + resp.WriteUint8(0) // NULL terminator. + } + doSizedAckResp(s, pkt.AckHandle, resp.Data()) } func handleMsgSysNotifyUserBinary(s *Session, p mhfpacket.MHFPacket) {} From cf91478abb2728d2dcdd77c2f20f6eb31118c56f Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Thu, 23 Jan 2020 21:07:29 -0500 Subject: [PATCH 26/49] Fix typo in MsgSysGetUserBinary --- server/channelserver/handlers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index a79cff94e..562cc4f95 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -563,8 +563,8 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysGetUserBinary) - - resp := bf.NewByteFrame() + + resp := byteframe.NewByteFrame() if pkt.BinaryType == 1 { // Stub name response with character ID resp.WriteBytes([]byte(fmt.Sprintf("CID%d", s.charID))) From 69ca0c13acdb973332a73ffd1e5aea14fb93a2bb Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Fri, 24 Jan 2020 13:38:38 -0500 Subject: [PATCH 27/49] Add temp user binary parts 2 & 3 --- server/channelserver/handlers.go | 33 ++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 562cc4f95..03f79915f 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -2,6 +2,7 @@ package channelserver import ( "bytes" + "encoding/base64" "fmt" "io/ioutil" "log" @@ -310,18 +311,14 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { CharID: s.charID, BinaryType: 1, }, s) - - // Just the first user binary type (name) for right now. - /* - s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ - CharID: s.charID, - BinaryType: 2, - }, s) - s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ - CharID: s.charID, - BinaryType: 3, - }, s) - */ + s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ + CharID: s.charID, + BinaryType: 2, + }, s) + s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ + CharID: s.charID, + BinaryType: 3, + }, s) // TODO(Andoryuuta): Notify this client about all of the existing clients in the stage. s.logger.Info("Notifying entree about existing stage clients") @@ -569,6 +566,18 @@ func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) { // Stub name response with character ID resp.WriteBytes([]byte(fmt.Sprintf("CID%d", s.charID))) resp.WriteUint8(0) // NULL terminator. + } else if pkt.BinaryType == 2 { + data, err := base64.StdEncoding.DecodeString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBn8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAwAAAAAAAAAAAAAABAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") + if err != nil { + panic(err) + } + resp.WriteBytes(data) + } else if pkt.BinaryType == 3 { + data, err := base64.StdEncoding.DecodeString("AQAAA2ea5P8ATgEA/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBn8AAAAAAAAAAAABAKAMAAAAAAAAAAAAACgAAAAAAAAAAAABAsQOAAAAAAAAAAABA6UMAAAAAAAAAAABBKAMAAAAAAAAAAABBToNAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + if err != nil { + panic(err) + } + resp.WriteBytes(data) } doSizedAckResp(s, pkt.AckHandle, resp.Data()) From 99ceeb687207e0c3373aa2d9378040017b163adb Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Fri, 24 Jan 2020 15:28:08 -0500 Subject: [PATCH 28/49] Fix entree object notifications --- server/channelserver/handlers.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 03f79915f..31d5f2d4a 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -320,7 +320,7 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { BinaryType: 3, }, s) - // TODO(Andoryuuta): Notify this client about all of the existing clients in the stage. + // Notify the entree client about all of the existing clients in the stage. s.logger.Info("Notifying entree about existing stage clients") s.stage.RLock() clientNotif := byteframe.NewByteFrame() @@ -329,11 +329,18 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { CharID: session.charID, }).Build(clientNotif) - // Just the first user binary type (name) for right now. (&mhfpacket.MsgSysNotifyUserBinary{ CharID: session.charID, BinaryType: 1, }).Build(clientNotif) + (&mhfpacket.MsgSysNotifyUserBinary{ + CharID: session.charID, + BinaryType: 2, + }).Build(clientNotif) + (&mhfpacket.MsgSysNotifyUserBinary{ + CharID: session.charID, + BinaryType: 3, + }).Build(clientNotif) } s.stage.RUnlock() clientNotif.WriteUint16(0x0010) // End it. From 1929b8823dd043fc0eeb558ccda83f9d67e3d781 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Sat, 1 Feb 2020 16:22:13 -0500 Subject: [PATCH 29/49] Write missing packet opcodes --- server/channelserver/handlers.go | 35 ++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 31d5f2d4a..a4226898c 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -295,7 +295,7 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { s.stage = newStage s.Unlock() - // Tell the client to cleanup it's current stage objects. + // Tell the client to cleanup its current stage objects. s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{}) // Confirm the stage entry. @@ -325,22 +325,33 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { s.stage.RLock() clientNotif := byteframe.NewByteFrame() for session := range s.stage.clients { - (&mhfpacket.MsgSysInsertUser{ + var cur mhfpacket.MHFPacket + cur = &mhfpacket.MsgSysInsertUser{ CharID: session.charID, - }).Build(clientNotif) + } + clientNotif.WriteUint16(uint16(cur.Opcode())) + cur.Build(clientNotif) - (&mhfpacket.MsgSysNotifyUserBinary{ + cur = &mhfpacket.MsgSysNotifyUserBinary{ CharID: session.charID, BinaryType: 1, - }).Build(clientNotif) - (&mhfpacket.MsgSysNotifyUserBinary{ + } + clientNotif.WriteUint16(uint16(cur.Opcode())) + cur.Build(clientNotif) + + cur = &mhfpacket.MsgSysNotifyUserBinary{ CharID: session.charID, BinaryType: 2, - }).Build(clientNotif) - (&mhfpacket.MsgSysNotifyUserBinary{ + } + clientNotif.WriteUint16(uint16(cur.Opcode())) + cur.Build(clientNotif) + + cur = &mhfpacket.MsgSysNotifyUserBinary{ CharID: session.charID, BinaryType: 3, - }).Build(clientNotif) + } + clientNotif.WriteUint16(uint16(cur.Opcode())) + cur.Build(clientNotif) } s.stage.RUnlock() clientNotif.WriteUint16(0x0010) // End it. @@ -351,14 +362,16 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { clientDupObjNotif := byteframe.NewByteFrame() s.stage.RLock() for _, obj := range s.stage.objects { - (&mhfpacket.MsgSysDuplicateObject{ + cur := &mhfpacket.MsgSysDuplicateObject{ ObjID: obj.id, X: obj.x, Y: obj.y, Z: obj.z, Unk0: 0, OwnerCharID: obj.ownerCharID, - }).Build(clientDupObjNotif) + } + clientDupObjNotif.WriteUint16(uint16(cur.Opcode())) + cur.Build(clientDupObjNotif) } s.stage.RUnlock() clientDupObjNotif.WriteUint16(0x0010) // End it. From eb491093f87906dce115cfab433aa4e6ceffdf7b Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Sat, 1 Feb 2020 17:40:47 -0500 Subject: [PATCH 30/49] Make custom launcher files a config option --- config.json | 3 ++- config/config.go | 1 + main.go | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/config.json b/config.json index 6dafbae4c..c0ace65dc 100644 --- a/config.json +++ b/config.json @@ -9,7 +9,8 @@ "database": "erupe" }, "launcher": { - "port": 80 + "port": 80, + "UseOriginalLauncherFiles": false }, "sign": { "port": 53312 diff --git a/config/config.go b/config/config.go index 5886386c6..94dbb78e4 100644 --- a/config/config.go +++ b/config/config.go @@ -30,6 +30,7 @@ type Database struct { // Launcher holds the launcher server config. type Launcher struct { Port int + UseOriginalLauncherFiles bool } // Sign holds the sign server config. diff --git a/main.go b/main.go index 296dd248e..3869b6489 100644 --- a/main.go +++ b/main.go @@ -71,7 +71,7 @@ func main() { Logger: logger.Named("launcher"), ErupeConfig: erupeConfig, DB: db, - UseOriginalLauncherFiles: false, + UseOriginalLauncherFiles: erupeConfig.Launcher.UseOriginalLauncherFiles, }) err = launcherServer.Start() if err != nil { From 0893f75c3f57ca23c6a4a1c7eaa855f54e40f312 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Sat, 1 Feb 2020 17:47:16 -0500 Subject: [PATCH 31/49] Update README.md --- README.md | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 65f41de81..944f1fcb0 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,61 @@ # Erupe ## WARNING -This project is in its infancy and currently doesn't do anything worth noting. Additionally, it has no documentation, no support, and cannot be used without binary resources that are not in the repo. +This project is in its infancy and currently doesn't do anything worth noting. Additionally, it has no documentation and no support. + +This project is soley developed in my spare time for the educational experience of making a server emulator, which I haven't done before. Expectations regarding functionally and code quality should be set accordingly. # General info -Originally based on the TW version, but (slowly) transitioning to JP. +Currently allows a JP MHF client (with GameGuard removed) to: +* Login and register an account (registration is automatic if account doesn't exist) +* Create a character +* Get ingame to the main city +* See other players walk around (Names and character data are not correct, placeholders are in use) +* Use (local) chat. # Installation 1. Clone the repo with `git clone https://github.com/Andoryuuta/Erupe.git` 2. Install PostgreSQL 3. Launch psql shell, `CREATE DATABASE erupe;`. - 4. Setup database with golang-migrate: -``` -> go get -tags 'postgres' -u github.com/golang-migrate/migrate/cmd/migrate -> set POSTGRESQL_URL=postgres://postgres:password@localhost:5432/erupe?sslmode=disable + Windows: + ``` + > go get -tags 'postgres' -u github.com/golang-migrate/migrate/cmd/migrate -> migrate -database %POSTGRESQL_URL% -path migrations up -``` + > set POSTGRESQL_URL=postgres://postgres:password@localhost:5432/erupe?sslmode=disable -5. Open psql shell and manually insert an account into the users table. + > cd erupe -6. Manually extract the binary response from a pcap, strip the header, and decrypt the ~50 packets that are used in `./channelserver/session.go`, and place them in `./bin_resp/{OPCODE}_resp.bin`. (**These are not included in the repo**) + > migrate -database %POSTGRESQL_URL% -path migrations up + ``` + Linux: + ``` + > go get -tags 'postgres' -u github.com/golang-migrate/migrate/cmd/migrate + > export POSTGRESQL_URL=postgres://postgres:password@localhost:5432/erupe?sslmode=disable + + > cd erupe + + > migrate -database $POSTGRESQL_URL -path migrations up + ``` + + (Replacing `postgres:password` with your postgres username and password) + +5. Edit the config.json + Namely: + * Update the database username and password + * Update the `host_ip` and `ip` fields (there are multiple) to your external IP if you are hosting for multiple clients. ## Launcher -Erupe now ships with a rudimentary custom launcher, so you don't need to obtain the original TW/JP files to simply get ingame. However, it does still support using the original files if you choose to. To set this up, place a copy of the original launcher html/js/css in `./www/tw/`, and `/www/jp/` for the TW and JP files respectively. +Erupe ships with a rudimentary custom launcher, so you don't need to obtain the original TW/JP files to simply get ingame. However, it does still support using the original files if you choose to. To set this up, place a copy of the original launcher html/js/css in `./www/tw/`, and `/www/jp/` for the TW and JP files respectively. Then, modify the the `/launcher/js/launcher.js` file as such: * Find the call to `startUpdateProcess();` in a case statement and replace it with `finishUpdateProcess();`. (This disables the file check and updating) * (JP ONLY): replace all uses of "https://" with "http://" in the file. -Finally, edit `main.go` and change: - -`go serveLauncherHTML(":80", false)` - -to: - -`go serveLauncherHTML(":80", true)` +Finally, edit the config.json and set `UseOriginalLauncherFiles` to `true` under the launcher settings. # Usage ### Note: If you are switching to/from the custom launcher html, you will have to clear your IE cache @ `C:\Users\\AppData\Local\Microsoft\Windows\INetCache`. From 773429ce15a1ca2392a145b8832dc8bcd3e1900c Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Mon, 3 Feb 2020 14:07:33 -0500 Subject: [PATCH 32/49] Make char savedata persistent --- migrations/000003_character_savedata.down.sql | 6 ++++++ migrations/000003_character_savedata.up.sql | 6 ++++++ network/mhfpacket/msg_mhf_loaddata.go | 9 +++++--- server/channelserver/handlers.go | 21 ++++++++++++++++--- 4 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 migrations/000003_character_savedata.down.sql create mode 100644 migrations/000003_character_savedata.up.sql diff --git a/migrations/000003_character_savedata.down.sql b/migrations/000003_character_savedata.down.sql new file mode 100644 index 000000000..d8d1ca8c6 --- /dev/null +++ b/migrations/000003_character_savedata.down.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE characters + DROP COLUMN savedata; + +END; \ No newline at end of file diff --git a/migrations/000003_character_savedata.up.sql b/migrations/000003_character_savedata.up.sql new file mode 100644 index 000000000..d0f39a223 --- /dev/null +++ b/migrations/000003_character_savedata.up.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE characters + ADD COLUMN savedata bytea; + +END; \ No newline at end of file diff --git a/network/mhfpacket/msg_mhf_loaddata.go b/network/mhfpacket/msg_mhf_loaddata.go index f34ec6b96..0cb5499f2 100644 --- a/network/mhfpacket/msg_mhf_loaddata.go +++ b/network/mhfpacket/msg_mhf_loaddata.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfLoaddata represents the MSG_MHF_LOADDATA -type MsgMhfLoaddata struct{} +type MsgMhfLoaddata struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfLoaddata) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfLoaddata) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfLoaddata) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfLoaddata) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index a4226898c..eae2f1a4c 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "fmt" "io/ioutil" - "log" "strings" "time" @@ -633,12 +632,28 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSavedata) err := ioutil.WriteFile(fmt.Sprintf("savedata\\%d.bin", time.Now().Unix()), pkt.RawDataPayload, 0644) if err != nil { - log.Fatal(err) + s.logger.Fatal("Error dumping savedata", zap.Error(err)) } + + _, err = s.server.db.Exec("UPDATE characters SET is_new_character=false, savedata=$1 WHERE id=$2", pkt.RawDataPayload, s.charID) + if err != nil { + s.logger.Fatal("Failed to update savedata in db", zap.Error(err)) + } + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) } -func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfLoaddata) + + var data []byte + err := s.server.db.QueryRow("SELECT savedata FROM characters WHERE id = $1", s.charID).Scan(&data) + if err != nil { + s.logger.Fatal("Failed to get savedata from db", zap.Error(err)) + } + + doSizedAckResp(s, pkt.AckHandle, data) +} func handleMsgMhfListMember(s *Session, p mhfpacket.MHFPacket) {} From f17d1db9a752d11bf645e5c6d7511add2d85fa9f Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Mon, 3 Feb 2020 15:25:04 -0500 Subject: [PATCH 33/49] Implement packets & handlers for loading existing character --- .../msg_mhf_enumerate_guild_member.go | 15 +++++-- network/mhfpacket/msg_mhf_info_guild.go | 11 +++-- network/mhfpacket/msg_mhf_list_member.go | 9 +++- network/mhfpacket/msg_mhf_state_festa_g.go | 14 ++++++- server/channelserver/handlers.go | 41 +++++++++++++++++-- 5 files changed, 76 insertions(+), 14 deletions(-) diff --git a/network/mhfpacket/msg_mhf_enumerate_guild_member.go b/network/mhfpacket/msg_mhf_enumerate_guild_member.go index f30f24319..3167ca951 100644 --- a/network/mhfpacket/msg_mhf_enumerate_guild_member.go +++ b/network/mhfpacket/msg_mhf_enumerate_guild_member.go @@ -6,7 +6,12 @@ import ( ) // MsgMhfEnumerateGuildMember represents the MSG_MHF_ENUMERATE_GUILD_MEMBER -type MsgMhfEnumerateGuildMember struct{} +type MsgMhfEnumerateGuildMember struct { + AckHandle uint32 + Unk0 uint16 // Hardcoed 00 01 in the binary + Unk1 uint32 + Unk2 uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfEnumerateGuildMember) Opcode() network.PacketID { @@ -15,10 +20,14 @@ func (m *MsgMhfEnumerateGuildMember) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfEnumerateGuildMember) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint32() + m.Unk2 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfEnumerateGuildMember) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_info_guild.go b/network/mhfpacket/msg_mhf_info_guild.go index dad8df62a..111831cac 100644 --- a/network/mhfpacket/msg_mhf_info_guild.go +++ b/network/mhfpacket/msg_mhf_info_guild.go @@ -6,7 +6,10 @@ import ( ) // MsgMhfInfoGuild represents the MSG_MHF_INFO_GUILD -type MsgMhfInfoGuild struct{} +type MsgMhfInfoGuild struct { + AckHandle uint32 + Unk0 uint32 // Probably a guild ID, but unverified. +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfInfoGuild) Opcode() network.PacketID { @@ -15,10 +18,12 @@ func (m *MsgMhfInfoGuild) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfInfoGuild) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfInfoGuild) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_list_member.go b/network/mhfpacket/msg_mhf_list_member.go index 1d1023bdf..d4759378d 100644 --- a/network/mhfpacket/msg_mhf_list_member.go +++ b/network/mhfpacket/msg_mhf_list_member.go @@ -6,7 +6,10 @@ import ( ) // MsgMhfListMember represents the MSG_MHF_LIST_MEMBER -type MsgMhfListMember struct{} +type MsgMhfListMember struct{ + AckHandle uint32 + Unk0 uint16 // Hardcoded 01 00 in the JP client. +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfListMember) Opcode() network.PacketID { @@ -15,7 +18,9 @@ func (m *MsgMhfListMember) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfListMember) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_state_festa_g.go b/network/mhfpacket/msg_mhf_state_festa_g.go index ba5d7158f..5651b611d 100644 --- a/network/mhfpacket/msg_mhf_state_festa_g.go +++ b/network/mhfpacket/msg_mhf_state_festa_g.go @@ -6,7 +6,12 @@ import ( ) // MsgMhfStateFestaG represents the MSG_MHF_STATE_FESTA_G -type MsgMhfStateFestaG struct{} +type MsgMhfStateFestaG struct{ + AckHandle uint32 + Unk0 uint32 // Shared ID of something. + Unk1 uint32 + Unk2 uint16 // Hardcoded 0 in the binary. +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfStateFestaG) Opcode() network.PacketID { @@ -15,7 +20,12 @@ func (m *MsgMhfStateFestaG) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfStateFestaG) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.Unk1 = bf.ReadUint32() + m.Unk2 = bf.ReadUint16() + + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index eae2f1a4c..7b1547247 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -655,7 +655,14 @@ func handleMsgMhfLoaddata(s *Session, p mhfpacket.MHFPacket) { doSizedAckResp(s, pkt.AckHandle, data) } -func handleMsgMhfListMember(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfListMember(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfListMember) + + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) // Members count. (Unsure of what kind of members these actually are, guild, party, COG subscribers, etc.) + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfOprMember(s *Session, p mhfpacket.MHFPacket) {} @@ -757,7 +764,16 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfOperateGuildMember(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfInfoGuild) + + // REALLY large/complex format... stubbing it out here for simplicity. + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) // Count + resp.WriteUint8(0) // Unk, read if count == 0. + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {} @@ -765,7 +781,10 @@ func handleMsgMhfUpdateGuild(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfArrangeGuildMember(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfEnumerateGuildMember) + stubEnumerateNoResults(s, pkt.AckHandle) +} func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {} @@ -868,9 +887,23 @@ func handleMsgMhfChargeFesta(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfAcquireFesta(s *Session, p mhfpacket.MHFPacket) {} +// state festa (U)ser func handleMsgMhfStateFestaU(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) {} +// state festa (G)uild +func handleMsgMhfStateFestaG(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfStateFestaG) + + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0xFFFFFFFF) + resp.WriteUint32(0) + resp.WriteBytes([]byte{0x00, 0x00, 0x00}) // Not parsed. + resp.WriteUint8(0) + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfEnumerateFestaMember(s *Session, p mhfpacket.MHFPacket) {} From 5924db9b424e37e248b7338485d9a2933dcb4709 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Mon, 3 Feb 2020 15:27:38 -0500 Subject: [PATCH 34/49] Remove debug code from entrance server --- server/entranceserver/make_resp.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/server/entranceserver/make_resp.go b/server/entranceserver/make_resp.go index 0a2ab71c2..73ac12ea6 100644 --- a/server/entranceserver/make_resp.go +++ b/server/entranceserver/make_resp.go @@ -2,9 +2,6 @@ package entranceserver import ( "encoding/binary" - "fmt" - "io/ioutil" - "log" "net" "github.com/Andoryuuta/Erupe/config" @@ -35,7 +32,6 @@ func encodeServerInfo(serverInfos []config.EntranceServerInfo) []byte { bf.WriteUint32(si.AllowedClientFlags) for channelIdx, ci := range si.Channels { - fmt.Println("Channel idx", channelIdx) bf.WriteUint16(ci.Port) bf.WriteUint16(16 + uint16(channelIdx)) bf.WriteUint16(ci.MaxPlayers) @@ -75,7 +71,6 @@ func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byt } func makeResp(servers []config.EntranceServerInfo) []byte { - fmt.Printf("%+v\n", servers) rawServerData := encodeServerInfo(servers) bf := byteframe.NewByteFrame() @@ -86,11 +81,6 @@ func makeResp(servers []config.EntranceServerInfo) []byte { // If so, how does it work without the entrance server connection being authenticated? bf.WriteBytes(makeHeader([]byte{}, "USR", 0, 0x00)) - err := ioutil.WriteFile("go_entrance_resp.bin", bf.Data(), 0644) - if err != nil { - log.Fatal(err) - } - return bf.Data() } From 4e1cef9bf37d3965725a1fedc678d252f9db0712 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Mon, 3 Feb 2020 22:56:54 -0500 Subject: [PATCH 35/49] Implement stage binaries, stack locking, and logkeys --- network/mhfpacket/msg_sys_get_stage_binary.go | 19 ++++- network/mhfpacket/msg_sys_issue_logkey.go | 13 ++- network/mhfpacket/msg_sys_lock_stage.go | 17 +++- network/mhfpacket/msg_sys_set_stage_binary.go | 8 +- network/mhfpacket/msg_sys_unlock_stage.go | 5 +- server/channelserver/handlers.go | 81 ++++++++++++++++++- server/channelserver/session.go | 1 + server/channelserver/stage.go | 15 +++- 8 files changed, 136 insertions(+), 23 deletions(-) diff --git a/network/mhfpacket/msg_sys_get_stage_binary.go b/network/mhfpacket/msg_sys_get_stage_binary.go index fa21aab36..a6368567c 100644 --- a/network/mhfpacket/msg_sys_get_stage_binary.go +++ b/network/mhfpacket/msg_sys_get_stage_binary.go @@ -6,7 +6,14 @@ import ( ) // MsgSysGetStageBinary represents the MSG_SYS_GET_STAGE_BINARY -type MsgSysGetStageBinary struct{} +type MsgSysGetStageBinary struct { + AckHandle uint32 + BinaryType0 uint8 + BinaryType1 uint8 + Unk0 uint32 // Hardcoded 0 + StageIDLength uint8 + StageID string +} // Opcode returns the ID associated with this packet type. func (m *MsgSysGetStageBinary) Opcode() network.PacketID { @@ -15,10 +22,16 @@ func (m *MsgSysGetStageBinary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysGetStageBinary) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.BinaryType0 = bf.ReadUint8() + m.BinaryType1 = bf.ReadUint8() + m.Unk0 = bf.ReadUint32() + m.StageIDLength = bf.ReadUint8() + m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysGetStageBinary) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_issue_logkey.go b/network/mhfpacket/msg_sys_issue_logkey.go index b8d95ddb0..2114b4d12 100644 --- a/network/mhfpacket/msg_sys_issue_logkey.go +++ b/network/mhfpacket/msg_sys_issue_logkey.go @@ -6,7 +6,11 @@ import ( ) // MsgSysIssueLogkey represents the MSG_SYS_ISSUE_LOGKEY -type MsgSysIssueLogkey struct{} +type MsgSysIssueLogkey struct { + AckHandle uint32 + Unk0 uint16 // Hardcoded 00 01 in binary + Unk1 uint16 // Hardcoded 0 in binary. +} // Opcode returns the ID associated with this packet type. func (m *MsgSysIssueLogkey) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgSysIssueLogkey) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysIssueLogkey) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint16() + m.Unk1 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. func (m *MsgSysIssueLogkey) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_lock_stage.go b/network/mhfpacket/msg_sys_lock_stage.go index 7968ef332..085aadc46 100644 --- a/network/mhfpacket/msg_sys_lock_stage.go +++ b/network/mhfpacket/msg_sys_lock_stage.go @@ -6,7 +6,13 @@ import ( ) // MsgSysLockStage represents the MSG_SYS_LOCK_STAGE -type MsgSysLockStage struct{} +type MsgSysLockStage struct { + AckHandle uint32 + Unk0 uint8 // Hardcoded 1 in the binary + Unk1 uint8 // Hardcoded 1 in the binary + StageIDLength uint8 + StageID string +} // Opcode returns the ID associated with this packet type. func (m *MsgSysLockStage) Opcode() network.PacketID { @@ -15,10 +21,15 @@ func (m *MsgSysLockStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysLockStage) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint8() + m.StageIDLength = bf.ReadUint8() + m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysLockStage) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_set_stage_binary.go b/network/mhfpacket/msg_sys_set_stage_binary.go index 40d9e57bc..4f087b2ac 100644 --- a/network/mhfpacket/msg_sys_set_stage_binary.go +++ b/network/mhfpacket/msg_sys_set_stage_binary.go @@ -7,8 +7,8 @@ import ( // MsgSysSetStageBinary represents the MSG_SYS_SET_STAGE_BINARY type MsgSysSetStageBinary struct { - Unk0 uint8 - BinaryType uint8 // Index + BinaryType0 uint8 + BinaryType1 uint8 // Index StageIDLength uint8 // <= 0x20 DataSize uint16 // <= 0x400 StageID string @@ -22,8 +22,8 @@ func (m *MsgSysSetStageBinary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysSetStageBinary) Parse(bf *byteframe.ByteFrame) error { - m.Unk0 = bf.ReadUint8() - m.BinaryType = bf.ReadUint8() + m.BinaryType0 = bf.ReadUint8() + m.BinaryType1 = bf.ReadUint8() m.StageIDLength = bf.ReadUint8() m.DataSize = bf.ReadUint16() m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) diff --git a/network/mhfpacket/msg_sys_unlock_stage.go b/network/mhfpacket/msg_sys_unlock_stage.go index 043ba6ce3..c53c46737 100644 --- a/network/mhfpacket/msg_sys_unlock_stage.go +++ b/network/mhfpacket/msg_sys_unlock_stage.go @@ -15,10 +15,11 @@ func (m *MsgSysUnlockStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysUnlockStage) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + // No data + return nil } // Build builds a binary packet from the current data. func (m *MsgSysUnlockStage) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 7b1547247..e5becd9d4 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -2,6 +2,7 @@ package channelserver import ( "bytes" + "crypto/rand" "encoding/base64" "fmt" "io/ioutil" @@ -246,7 +247,26 @@ func handleMsgSysCastedBinary(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysIssueLogkey) + + // Make a random log key for this session. + logKey := make([]byte, 8) + _, err := rand.Read(logKey) + if err != nil { + panic(err) + } + + s.Lock() + s.logKey = logKey + s.Unlock() + + // Issue it. + resp := byteframe.NewByteFrame() + resp.WriteBytes(logKey) + resp.WriteBytes([]byte{0x8E, 0x8E}) // Unk + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgSysRecordLog(s *Session, p mhfpacket.MHFPacket) {} @@ -383,7 +403,11 @@ func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) {} 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) + // TODO(Andoryuuta): What does this packet _actually_ do? + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {} @@ -403,9 +427,56 @@ func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysSetStageBinary(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysSetStageBinary(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysSetStageBinary) -func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) {} + // Try to get the stage + stageID := stripNullTerminator(pkt.StageID) + s.server.stagesLock.Lock() + stage, gotStage := s.server.stages[stageID] + s.server.stagesLock.Unlock() + + // If we got the stage, lock and set the data. + if gotStage { + stage.Lock() + stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}] = pkt.RawDataPayload + stage.Unlock() + } else { + s.logger.Warn("Failed to get stage", zap.String("StageID", stageID)) + } + s.logger.Debug("handleMsgSysSetStageBinary Done!") +} + +func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysGetStageBinary) + + // Try to get the stage + stageID := stripNullTerminator(pkt.StageID) + s.server.stagesLock.Lock() + stage, gotStage := s.server.stages[stageID] + s.server.stagesLock.Unlock() + + // If we got the stage, lock and try to get the data. + var stageBinary []byte + var gotBinary bool + if gotStage { + stage.Lock() + stageBinary, gotBinary = stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}] + stage.Unlock() + } else { + s.logger.Warn("Failed to get stage", zap.String("StageID", stageID)) + } + + if gotBinary { + doSizedAckResp(s, pkt.AckHandle, stageBinary) + } else { + s.logger.Warn("Failed to get stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1)) + s.logger.Warn("Sending blank stage binary") + doSizedAckResp(s, pkt.AckHandle, []byte{}) + } + + s.logger.Debug("MsgSysGetStageBinary Done!") +} func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysEnumerateClient) @@ -431,6 +502,8 @@ func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) { stage.RUnlock() doSizedAckResp(s, pkt.AckHandle, resp.Data()) + + s.logger.Debug("MsgSysEnumerateClient Done!") } func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/session.go b/server/channelserver/session.go index 0a6b6c8ee..352a895c6 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -24,6 +24,7 @@ type Session struct { stageID string stage *Stage charID uint32 + logKey []byte } // NewSession creates a new Session type. diff --git a/server/channelserver/stage.go b/server/channelserver/stage.go index 219749fab..1b996b1a1 100644 --- a/server/channelserver/stage.go +++ b/server/channelserver/stage.go @@ -15,13 +15,19 @@ type StageObject struct { x, y, z float32 } +type stageBinaryKey struct { + id0 uint8 + id1 uint8 +} + // Stage holds stage-specific information type Stage struct { sync.RWMutex - id string // Stage ID string - gameObjectCount uint32 // Total count of objects ever created for this stage. Used for ObjID generation. - objects map[uint32]*StageObject // Map of ObjID -> StageObject - clients map[*Session]uint32 // Map of session -> charID + id string // Stage ID string + gameObjectCount uint32 // Total count of objects ever created for this stage. Used for ObjID generation. + objects map[uint32]*StageObject // Map of ObjID -> StageObject + clients map[*Session]uint32 // Map of session -> charID + rawBinaryData map[stageBinaryKey][]byte // Raw binary data set by the client. } // NewStage creates a new stage with intialized values. @@ -30,6 +36,7 @@ func NewStage(ID string) *Stage { id: ID, objects: make(map[uint32]*StageObject), clients: make(map[*Session]uint32), + rawBinaryData: make(map[stageBinaryKey][]byte), gameObjectCount: 1, } From d4370c66ad96937a25ca9d7b1fcfe1dc49ad998b Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Wed, 5 Feb 2020 05:00:20 -0500 Subject: [PATCH 36/49] Add MSG_SYS_END to all sent packets --- server/channelserver/session.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/server/channelserver/session.go b/server/channelserver/session.go index 352a895c6..1d6c62545 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -93,11 +93,20 @@ func (s *Session) sendLoop() { for { // TODO(Andoryuuta): Test making this into a buffered channel and grouping the packet together before sending. rawPacket := <-s.sendPackets + if rawPacket == nil { s.logger.Debug("Got nil from s.SendPackets, exiting send loop") return } - s.cryptConn.SendPacket(rawPacket) + + // Make a copy of the data. + terminatedPacket := make([]byte, len(rawPacket)) + copy(terminatedPacket, rawPacket) + + // Append the MSG_SYS_END tailing opcode. + terminatedPacket = append(terminatedPacket, []byte{0x00, 0x10}...) + + s.cryptConn.SendPacket(terminatedPacket) } } From 8b65fc74950dba15c5d6970eb8e81d93ca257890 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Wed, 5 Feb 2020 05:03:28 -0500 Subject: [PATCH 37/49] Fix client crash and implement quest loading Fixed client crashes caused by handleMsgMhfEnumeratePrice and handleMsgMhfEnumerateRanking wherein the server's response didn't contain enough data, causing the client to read uninitalized memory. Implemented quest loading handlers GetFile, WaitStageBinary, and UnlockStage, as well as correcting the IssueLogkey handler. --- .gitignore | 9 +- .../__PLACE_YOUR_QUEST_.bin_FILES_HERE__.txt | 2 + config.json | 1 + config/config.go | 1 + network/mhfpacket/msg_sys_get_file.go | 32 +++++- network/mhfpacket/msg_sys_unlock_stage.go | 6 +- .../mhfpacket/msg_sys_wait_stage_binary.go | 17 ++- server/channelserver/handlers.go | 103 ++++++++++++++++-- 8 files changed, 147 insertions(+), 24 deletions(-) create mode 100644 bin/quests/__PLACE_YOUR_QUEST_.bin_FILES_HERE__.txt diff --git a/.gitignore b/.gitignore index be42fbcb3..2310f3074 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,6 @@ www/tw/ www/jp/ -bin_resp/ +bin/quests/*.bin savedata/ -custom_entrance_server_resp.bin -dec_bin8_data_dump.bin -entrance_resp_bin8_encrypted.bin -tw_server_list_resp.bin -Erupe.exe -test.py \ No newline at end of file +Erupe.exe \ No newline at end of file diff --git a/bin/quests/__PLACE_YOUR_QUEST_.bin_FILES_HERE__.txt b/bin/quests/__PLACE_YOUR_QUEST_.bin_FILES_HERE__.txt new file mode 100644 index 000000000..aa01a92bf --- /dev/null +++ b/bin/quests/__PLACE_YOUR_QUEST_.bin_FILES_HERE__.txt @@ -0,0 +1,2 @@ +place your quest .bin files here +e.g. 22031d0.bin \ No newline at end of file diff --git a/config.json b/config.json index c0ace65dc..28d079a77 100644 --- a/config.json +++ b/config.json @@ -1,5 +1,6 @@ { "host_ip": "127.0.0.1", + "bin_path": "bin", "database": { "host": "localhost", diff --git a/config/config.go b/config/config.go index 94dbb78e4..816a332e1 100644 --- a/config/config.go +++ b/config/config.go @@ -10,6 +10,7 @@ import ( // Config holds the global server-wide config. type Config struct { HostIP string `mapstructure:"host_ip"` + BinPath string `mapstructure:"bin_path"` Database Database Launcher Launcher diff --git a/network/mhfpacket/msg_sys_get_file.go b/network/mhfpacket/msg_sys_get_file.go index 30a4f0403..2d965f1ac 100644 --- a/network/mhfpacket/msg_sys_get_file.go +++ b/network/mhfpacket/msg_sys_get_file.go @@ -5,8 +5,21 @@ import ( "github.com/Andoryuuta/byteframe" ) +type scenarioFileIdentifer struct { + Unk0 uint8 + Unk1 uint32 + Unk2 uint8 + Unk3 uint8 +} + // MsgSysGetFile represents the MSG_SYS_GET_FILE -type MsgSysGetFile struct{} +type MsgSysGetFile struct { + AckHandle uint32 + IsScenario bool + FilenameLength uint8 + Filename string + ScenarioIdentifer scenarioFileIdentifer +} // Opcode returns the ID associated with this packet type. func (m *MsgSysGetFile) Opcode() network.PacketID { @@ -15,10 +28,23 @@ func (m *MsgSysGetFile) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysGetFile) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.IsScenario = bf.ReadBool() + if m.IsScenario { + m.ScenarioIdentifer = scenarioFileIdentifer{ + bf.ReadUint8(), + bf.ReadUint32(), + bf.ReadUint8(), + bf.ReadUint8(), + } + } else { + m.FilenameLength = bf.ReadUint8() + m.Filename = string(bf.ReadBytes(uint(m.FilenameLength))) + } + return nil } // Build builds a binary packet from the current data. func (m *MsgSysGetFile) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_unlock_stage.go b/network/mhfpacket/msg_sys_unlock_stage.go index c53c46737..054bb9574 100644 --- a/network/mhfpacket/msg_sys_unlock_stage.go +++ b/network/mhfpacket/msg_sys_unlock_stage.go @@ -6,7 +6,9 @@ import ( ) // MsgSysUnlockStage represents the MSG_SYS_UNLOCK_STAGE -type MsgSysUnlockStage struct{} +type MsgSysUnlockStage struct { + Unk0 uint16 // Hardcoded 0 in the binary. +} // Opcode returns the ID associated with this packet type. func (m *MsgSysUnlockStage) Opcode() network.PacketID { @@ -15,7 +17,7 @@ func (m *MsgSysUnlockStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysUnlockStage) Parse(bf *byteframe.ByteFrame) error { - // No data + m.Unk0 = bf.ReadUint16() return nil } diff --git a/network/mhfpacket/msg_sys_wait_stage_binary.go b/network/mhfpacket/msg_sys_wait_stage_binary.go index caa4b0f5b..b897b527d 100644 --- a/network/mhfpacket/msg_sys_wait_stage_binary.go +++ b/network/mhfpacket/msg_sys_wait_stage_binary.go @@ -6,7 +6,14 @@ import ( ) // MsgSysWaitStageBinary represents the MSG_SYS_WAIT_STAGE_BINARY -type MsgSysWaitStageBinary struct{} +type MsgSysWaitStageBinary struct{ + AckHandle uint32 + BinaryType0 uint8 + BinaryType1 uint8 + Unk0 uint32 // Hardcoded 0 + StageIDLength uint8 + StageID string +} // Opcode returns the ID associated with this packet type. func (m *MsgSysWaitStageBinary) Opcode() network.PacketID { @@ -15,7 +22,13 @@ func (m *MsgSysWaitStageBinary) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysWaitStageBinary) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.BinaryType0 = bf.ReadUint8() + m.BinaryType1 = bf.ReadUint8() + m.Unk0 = bf.ReadUint32() + m.StageIDLength = bf.ReadUint8() + m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) + return nil } // Build builds a binary packet from the current data. diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index e5becd9d4..d2329b3a4 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -6,6 +6,7 @@ import ( "encoding/base64" "fmt" "io/ioutil" + "path/filepath" "strings" "time" @@ -245,18 +246,35 @@ func handleMsgSysTime(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysCastedBinary(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysGetFile) + + if !pkt.IsScenario { + // Get quest file. + data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", stripNullTerminator(pkt.Filename)))) + if err != nil { + panic(err) + } + + doSizedAckResp(s, pkt.AckHandle, data) + } else { + s.logger.Fatal("scenario getfile not implemented.") + } + +} func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysIssueLogkey) // Make a random log key for this session. - logKey := make([]byte, 8) + logKey := make([]byte, 16) _, err := rand.Read(logKey) if err != nil { panic(err) } + // TODO(Andoryuuta): In the offical client, the log key index is off by one, + // cutting off the last byte in _most uses_. Find and document these accordingly. s.Lock() s.logKey = logKey s.Unlock() @@ -264,7 +282,6 @@ func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) { // Issue it. resp := byteframe.NewByteFrame() resp.WriteBytes(logKey) - resp.WriteBytes([]byte{0x8E, 0x8E}) // Unk doSizedAckResp(s, pkt.AckHandle, resp.Data()) } @@ -425,7 +442,48 @@ func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysWaitStageBinary) + defer s.logger.Debug("MsgSysWaitStageBinary Done!") + + // Try to get the stage + stageID := stripNullTerminator(pkt.StageID) + s.server.stagesLock.Lock() + stage, gotStage := s.server.stages[stageID] + s.server.stagesLock.Unlock() + + // If we got the stage, lock and try to get the data. + var stageBinary []byte + var gotBinary bool + if gotStage { + for { + stage.Lock() + stageBinary, gotBinary = stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}] + stage.Unlock() + + if gotBinary { + doSizedAckResp(s, pkt.AckHandle, stageBinary) + break + } else { + // Couldn't get binary, sleep for some time and try again. + time.Sleep(2 * time.Second) + continue + } + + // TODO(Andoryuuta): Figure out what the game sends on timeout and implement it! + /* + if timeout { + s.logger.Warn("Failed to get stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1)) + s.logger.Warn("Sending blank stage binary") + doSizedAckResp(s, pkt.AckHandle, []byte{}) + return + } + */ + } + } else { + s.logger.Warn("Failed to get stage", zap.String("StageID", stageID)) + } +} func handleMsgSysSetStageBinary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysSetStageBinary) @@ -495,14 +553,22 @@ func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) { // Read-lock the stage and make the response with all of the charID's in the stage. resp := byteframe.NewByteFrame() stage.RLock() - resp.WriteUint16(uint16(len(stage.clients))) // Client count - for session := range stage.clients { - resp.WriteUint32(session.charID) // Client represented by charID + + // TODO(Andoryuuta): Add proper player-slot reservations for stages. + if len(stage.clients) >= 1 { + resp.WriteUint16(uint16(len(stage.clients))) // Client count + for session := range stage.clients { + resp.WriteUint32(session.charID) // Client represented by charID + } + } else { + // Just give our client. + resp.WriteUint16(1) + resp.WriteUint32(s.charID) } + stage.RUnlock() doSizedAckResp(s, pkt.AckHandle, resp.Data()) - s.logger.Debug("MsgSysEnumerateClient Done!") } @@ -524,6 +590,7 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { } doSizedAckResp(s, pkt.AckHandle, resp.Data()) + s.logger.Debug("handleMsgSysEnumerateStage Done!") } func handleMsgSysCreateMutex(s *Session, p mhfpacket.MHFPacket) {} @@ -890,12 +957,28 @@ func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumeratePrice(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumeratePrice) - stubEnumerateNoResults(s, pkt.AckHandle) + resp := byteframe.NewByteFrame() + resp.WriteUint16(0) // Entry type 1 count + resp.WriteUint16(0) // Entry type 2 count + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) } func handleMsgMhfEnumerateRanking(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfEnumerateRanking) - stubEnumerateNoResults(s, pkt.AckHandle) + + resp := byteframe.NewByteFrame() + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint32(0) + resp.WriteUint8(0) + resp.WriteUint8(0) // Some string length following this field. + resp.WriteUint16(0) // Entry type 1 count + resp.WriteUint8(0) // Entry type 2 count + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) // Update the client's rights as well: updateRights(s) From 50b21094f26a03fa6d6ad899a8439b15b28b2a1e Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Wed, 5 Feb 2020 13:15:26 -0500 Subject: [PATCH 38/49] Implement user binary parts propagation --- server/channelserver/channel_server.go | 24 ++++++++++---- server/channelserver/handlers.go | 44 +++++++++++++++++--------- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/server/channelserver/channel_server.go b/server/channelserver/channel_server.go index b2c2d9fdd..ed2db6d6a 100644 --- a/server/channelserver/channel_server.go +++ b/server/channelserver/channel_server.go @@ -19,6 +19,12 @@ type Config struct { ErupeConfig *config.Config } +// Map key type for a user binary part. +type userBinaryPartID struct { + charID uint32 + index uint8 +} + // Server is a MHF channel server. type Server struct { sync.Mutex @@ -34,18 +40,22 @@ type Server struct { stagesLock sync.RWMutex stages map[string]*Stage + + userBinaryPartsLock sync.RWMutex + userBinaryParts map[userBinaryPartID][]byte } // NewServer creates a new Server type. func NewServer(config *Config) *Server { s := &Server{ - logger: config.Logger, - db: config.DB, - erupeConfig: config.ErupeConfig, - acceptConns: make(chan net.Conn), - deleteConns: make(chan net.Conn), - sessions: make(map[net.Conn]*Session), - stages: make(map[string]*Stage), + logger: config.Logger, + db: config.DB, + erupeConfig: config.ErupeConfig, + acceptConns: make(chan net.Conn), + deleteConns: make(chan net.Conn), + sessions: make(map[net.Conn]*Session), + stages: make(map[string]*Stage), + userBinaryParts: make(map[userBinaryPartID][]byte), } // Default town stage that clients try to enter without creating. diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index d2329b3a4..46216297f 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -714,28 +714,42 @@ func handleMsgSysInsertUser(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysDeleteUser(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) { - //pkt := p.(*mhfpacket.MsgSysSetUserBinary) + pkt := p.(*mhfpacket.MsgSysSetUserBinary) + s.server.userBinaryPartsLock.Lock() + s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: pkt.BinaryType}] = pkt.RawDataPayload + s.server.userBinaryPartsLock.Unlock() } func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysGetUserBinary) + // Try to get the data. + s.server.userBinaryPartsLock.RLock() + defer s.server.userBinaryPartsLock.RUnlock() + data, ok := s.server.userBinaryParts[userBinaryPartID{charID: pkt.CharID, index: pkt.BinaryType}] + resp := byteframe.NewByteFrame() - if pkt.BinaryType == 1 { - // Stub name response with character ID - resp.WriteBytes([]byte(fmt.Sprintf("CID%d", s.charID))) - resp.WriteUint8(0) // NULL terminator. - } else if pkt.BinaryType == 2 { - data, err := base64.StdEncoding.DecodeString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBn8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAwAAAAAAAAAAAAAABAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") - if err != nil { - panic(err) - } - resp.WriteBytes(data) - } else if pkt.BinaryType == 3 { - data, err := base64.StdEncoding.DecodeString("AQAAA2ea5P8ATgEA/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBn8AAAAAAAAAAAABAKAMAAAAAAAAAAAAACgAAAAAAAAAAAABAsQOAAAAAAAAAAABA6UMAAAAAAAAAAABBKAMAAAAAAAAAAABBToNAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") - if err != nil { - panic(err) + + // If we can't get the real data, use a placeholder. + if !ok { + if pkt.BinaryType == 1 { + // Stub name response with character ID + resp.WriteBytes([]byte(fmt.Sprintf("CID%d", s.charID))) + resp.WriteUint8(0) // NULL terminator. + } else if pkt.BinaryType == 2 { + data, err := base64.StdEncoding.DecodeString("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBn8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAwAAAAAAAAAAAAAABAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==") + if err != nil { + panic(err) + } + resp.WriteBytes(data) + } else if pkt.BinaryType == 3 { + data, err := base64.StdEncoding.DecodeString("AQAAA2ea5P8ATgEA/wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBn8AAAAAAAAAAAABAKAMAAAAAAAAAAAAACgAAAAAAAAAAAABAsQOAAAAAAAAAAABA6UMAAAAAAAAAAABBKAMAAAAAAAAAAABBToNAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + if err != nil { + panic(err) + } + resp.WriteBytes(data) } + } else { resp.WriteBytes(data) } From 315449aa33849e05c305c129062e725c20a2d0fd Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Fri, 7 Feb 2020 13:36:24 -0500 Subject: [PATCH 39/49] Document MsgSysEnumerateStage resp fields --- server/channelserver/handlers.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 46216297f..bc6c51679 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -583,8 +583,13 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { resp := byteframe.NewByteFrame() resp.WriteUint16(uint16(len(s.server.stages))) for sid := range s.server.stages { - // Couldn't find the parsing code in the client, unk purpose & sizes: - resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00}) + // Found parsing code, field sizes are correct, but unknown purposes still. + //resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00}) + resp.WriteUint16(5) // Current players. + resp.WriteUint16(7) // Unknown value + resp.WriteUint16(0) // HasDeparted or IsLocked. + resp.WriteUint16(20) // Max players. + resp.WriteUint8(2) // Password protected. resp.WriteUint8(uint8(len(sid))) resp.WriteBytes([]byte(sid)) } @@ -727,7 +732,7 @@ func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) { s.server.userBinaryPartsLock.RLock() defer s.server.userBinaryPartsLock.RUnlock() data, ok := s.server.userBinaryParts[userBinaryPartID{charID: pkt.CharID, index: pkt.BinaryType}] - + resp := byteframe.NewByteFrame() // If we can't get the real data, use a placeholder. From 99c965e3b68f361b914e1132636841d338321342 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Fri, 7 Feb 2020 18:16:33 -0500 Subject: [PATCH 40/49] Combine MsgSysEnterStage with MsgSysMoveStage --- network/mhfpacket/msg_sys_move_stage.go | 15 ++++++++++++--- server/channelserver/channel_server.go | 4 ++++ server/channelserver/handlers.go | 23 +++++++++++++++-------- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/network/mhfpacket/msg_sys_move_stage.go b/network/mhfpacket/msg_sys_move_stage.go index 57bfdb942..c8ae87dd7 100644 --- a/network/mhfpacket/msg_sys_move_stage.go +++ b/network/mhfpacket/msg_sys_move_stage.go @@ -6,7 +6,12 @@ import ( ) // MsgSysMoveStage represents the MSG_SYS_MOVE_STAGE -type MsgSysMoveStage struct{} +type MsgSysMoveStage struct { + AckHandle uint32 + UnkBool uint8 + StageIDLength uint8 + StageID string +} // Opcode returns the ID associated with this packet type. func (m *MsgSysMoveStage) Opcode() network.PacketID { @@ -15,10 +20,14 @@ func (m *MsgSysMoveStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysMoveStage) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.UnkBool = bf.ReadUint8() + m.StageIDLength = bf.ReadUint8() + m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) + return nil } // Build builds a binary packet from the current data. func (m *MsgSysMoveStage) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/server/channelserver/channel_server.go b/server/channelserver/channel_server.go index ed2db6d6a..ca9452170 100644 --- a/server/channelserver/channel_server.go +++ b/server/channelserver/channel_server.go @@ -62,6 +62,10 @@ func NewServer(config *Config) *Server { stage := NewStage("sl1Ns200p0a0u0") s.stages[stage.id] = stage + // Town underground left area -- rasta bar stage (Maybe private bar ID as well?) + stage2 := NewStage("sl1Ns211p0a0u0") + s.stages[stage2.id] = stage2 + return s } diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index bc6c51679..5bfd965ca 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -303,12 +303,11 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysStageDestruct(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgSysEnterStage) +func doStageTransfer(s *Session, ackHandle uint32, stageID string) { // Remove this session from old stage clients list and put myself in the new one. s.server.stagesLock.Lock() - newStage, gotNewStage := s.server.stages[stripNullTerminator(pkt.StageID)] + newStage, gotNewStage := s.server.stages[stripNullTerminator(stageID)] s.server.stagesLock.Unlock() // Remove from old stage. @@ -327,7 +326,7 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { // Save our new stage ID and pointer to the new stage itself. s.Lock() - s.stageID = string(stripNullTerminator(pkt.StageID)) + s.stageID = string(stripNullTerminator(stageID)) s.stage = newStage s.Unlock() @@ -335,7 +334,7 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{}) // Confirm the stage entry. - s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + s.QueueAck(ackHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) // Notify existing stage clients that this new client has entered. s.logger.Info("Sending MsgSysInsertUser & MsgSysNotifyUserBinary") @@ -414,9 +413,17 @@ func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { s.QueueSend(clientDupObjNotif.Data()) } +func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysEnterStage) + doStageTransfer(s, pkt.AckHandle, pkt.StageID) +} + func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysMoveStage) + doStageTransfer(s, pkt.AckHandle, pkt.StageID) +} func handleMsgSysLeaveStage(s *Session, p mhfpacket.MHFPacket) {} @@ -587,9 +594,9 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { //resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00}) resp.WriteUint16(5) // Current players. resp.WriteUint16(7) // Unknown value - resp.WriteUint16(0) // HasDeparted or IsLocked. + resp.WriteUint16(0) // HasDeparted. resp.WriteUint16(20) // Max players. - resp.WriteUint8(2) // Password protected. + resp.WriteUint8(2) // Password protected. resp.WriteUint8(uint8(len(sid))) resp.WriteBytes([]byte(sid)) } From e5f26508710cfeb6e3a01e7e8bd4c757acaed3b9 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Wed, 19 Feb 2020 08:11:24 -0500 Subject: [PATCH 41/49] Add prayer/diva fountain stage --- server/channelserver/channel_server.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/channelserver/channel_server.go b/server/channelserver/channel_server.go index ca9452170..56c52b2cc 100644 --- a/server/channelserver/channel_server.go +++ b/server/channelserver/channel_server.go @@ -62,10 +62,14 @@ func NewServer(config *Config) *Server { stage := NewStage("sl1Ns200p0a0u0") s.stages[stage.id] = stage - // Town underground left area -- rasta bar stage (Maybe private bar ID as well?) + // Town underground left area -- rasta bar stage (Maybe private bar ID as well?). stage2 := NewStage("sl1Ns211p0a0u0") s.stages[stage2.id] = stage2 + // Diva fountain / prayer fountain. + stage3 := NewStage("sl2Ns379p0a0u0") + s.stages[stage3.id] = stage3 + return s } From 8cea6235c85304d94a208547cccdf9d8ef086263 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Wed, 19 Feb 2020 08:13:41 -0500 Subject: [PATCH 42/49] Implement stage object deletion --- network/mhfpacket/msg_sys_delete_object.go | 12 ++++++++---- server/channelserver/handlers.go | 20 ++++++++++++++++++-- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/network/mhfpacket/msg_sys_delete_object.go b/network/mhfpacket/msg_sys_delete_object.go index 817060e03..13d951103 100644 --- a/network/mhfpacket/msg_sys_delete_object.go +++ b/network/mhfpacket/msg_sys_delete_object.go @@ -6,7 +6,9 @@ import ( ) // MsgSysDeleteObject represents the MSG_SYS_DELETE_OBJECT -type MsgSysDeleteObject struct{} +type MsgSysDeleteObject struct { + ObjID uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysDeleteObject) Opcode() network.PacketID { @@ -15,10 +17,12 @@ func (m *MsgSysDeleteObject) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysDeleteObject) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.ObjID = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgSysDeleteObject) Build(bf *byteframe.ByteFrame) error { - panic("Not implemented") -} \ No newline at end of file + bf.WriteUint32(m.ObjID) + return nil +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 5bfd965ca..d75fbfc02 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -304,16 +304,32 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysStageDestruct(s *Session, p mhfpacket.MHFPacket) {} func doStageTransfer(s *Session, ackHandle uint32, stageID string) { - // Remove this session from old stage clients list and put myself in the new one. s.server.stagesLock.Lock() newStage, gotNewStage := s.server.stages[stripNullTerminator(stageID)] s.server.stagesLock.Unlock() - // Remove from old stage. if s.stage != nil { s.stage.Lock() + + // Remove client from old stage. delete(s.stage.clients, s) + + // Delete old stage objects owned by the client. + s.logger.Info("Sending MsgSysDeleteObject to old stage clients") + for objID, stageObject := range s.stage.objects { + if stageObject.ownerCharID == s.charID { + // Broadcast the deletion to clients in the stage. + s.stage.BroadcastMHF(&mhfpacket.MsgSysDeleteObject{ + ObjID: stageObject.id, + }, s) + // TODO(Andoryuuta): Should this be sent to the owner's client as well? it currently isn't. + + // Actually delete it form the objects map. + delete(s.stage.objects, objID) + } + } + s.stage.Unlock() } From fe786617d450861417268bc8a40fab0f0008dfae Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Fri, 21 Feb 2020 14:59:10 -0500 Subject: [PATCH 43/49] Implement scenario getfile packet and counter stub --- .gitignore | 2 + .../msg_mhf_info_scenario_counter.go | 9 +- network/mhfpacket/msg_sys_get_file.go | 30 +++++-- server/channelserver/handlers.go | 88 ++++++++++++++++++- 4 files changed, 117 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 2310f3074..04a9d02ea 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,7 @@ www/tw/ www/jp/ bin/quests/*.bin +bin/scenarios/*.bin +bin/debug/*.bin savedata/ Erupe.exe \ No newline at end of file diff --git a/network/mhfpacket/msg_mhf_info_scenario_counter.go b/network/mhfpacket/msg_mhf_info_scenario_counter.go index 9e38aa353..4a415a9e6 100644 --- a/network/mhfpacket/msg_mhf_info_scenario_counter.go +++ b/network/mhfpacket/msg_mhf_info_scenario_counter.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfInfoScenarioCounter represents the MSG_MHF_INFO_SCENARIO_COUNTER -type MsgMhfInfoScenarioCounter struct{} +type MsgMhfInfoScenarioCounter struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfInfoScenarioCounter) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfInfoScenarioCounter) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfInfoScenarioCounter) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfInfoScenarioCounter) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_get_file.go b/network/mhfpacket/msg_sys_get_file.go index 2d965f1ac..fccb1201b 100644 --- a/network/mhfpacket/msg_sys_get_file.go +++ b/network/mhfpacket/msg_sys_get_file.go @@ -6,10 +6,24 @@ import ( ) type scenarioFileIdentifer struct { - Unk0 uint8 - Unk1 uint32 - Unk2 uint8 - Unk3 uint8 + CategoryID uint8 + MainID uint32 + ChapterID uint8 + /* + Flags represent the following bit flags: + + 11111111 -> Least significant bit on the right. + |||||||| + |||||||0x1: Chunk0-type, recursive chunks, quest name/description + 0x14 byte unk info + ||||||0x2: Chunk1-type, recursive chunks, npc dialog? + 0x2C byte unk info + |||||0x4: UNK NONE FOUND. (Guessing from the following that this might be a chunk2-type) + ||||0x8: Chunk0-type, NO RECURSIVE CHUNKS ([0x1] prefixed?), Episode listing + |||0x10: Chunk1-type, NO RECURSIVE CHUNKS, JKR blob, npc dialog? + ||0x20: Chunk2-type, NO RECURSIVE CHUNKS, JKR blob, Menu options or quest titles? + |0x40: UNK NONE FOUND + 0x80: UNK NONE FOUND + */ + Flags uint8 } // MsgSysGetFile represents the MSG_SYS_GET_FILE @@ -30,6 +44,11 @@ func (m *MsgSysGetFile) Opcode() network.PacketID { func (m *MsgSysGetFile) Parse(bf *byteframe.ByteFrame) error { m.AckHandle = bf.ReadUint32() m.IsScenario = bf.ReadBool() + m.FilenameLength = bf.ReadUint8() + if m.FilenameLength > 0 { + m.Filename = string(bf.ReadBytes(uint(m.FilenameLength))) + } + if m.IsScenario { m.ScenarioIdentifer = scenarioFileIdentifer{ bf.ReadUint8(), @@ -37,9 +56,6 @@ func (m *MsgSysGetFile) Parse(bf *byteframe.ByteFrame) error { bf.ReadUint8(), bf.ReadUint8(), } - } else { - m.FilenameLength = bf.ReadUint8() - m.Filename = string(bf.ReadBytes(uint(m.FilenameLength))) } return nil } diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index d75fbfc02..0b68c15a6 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -249,6 +249,12 @@ func handleMsgSysCastedBinary(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysGetFile) + // Debug print the request. + fmt.Printf("%+v\n", pkt) + if pkt.IsScenario { + fmt.Printf("%+v\n", pkt.ScenarioIdentifer) + } + if !pkt.IsScenario { // Get quest file. data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", stripNullTerminator(pkt.Filename)))) @@ -258,7 +264,34 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) { doSizedAckResp(s, pkt.AckHandle, data) } else { - s.logger.Fatal("scenario getfile not implemented.") + + /* + // mhf-fake-client format + filename := fmt.Sprintf( + "%d_%d_%d_%d", + pkt.ScenarioIdentifer.CategoryID, + pkt.ScenarioIdentifer.MainID, + pkt.ScenarioIdentifer.ChapterID, + pkt.ScenarioIdentifer.Flags, + ) + */ + + // Fist's format: + filename := fmt.Sprintf( + "%d_0_0_0_S%d_T%d_C%d", + pkt.ScenarioIdentifer.CategoryID, + pkt.ScenarioIdentifer.MainID, + pkt.ScenarioIdentifer.Flags, // Fist had as "type" and is the "T%d" + pkt.ScenarioIdentifer.ChapterID, + ) + + // Read the scenario file. + data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("scenarios/%s.bin", filename))) + if err != nil { + panic(err) + } + + doSizedAckResp(s, pkt.AckHandle, data) } } @@ -1175,7 +1208,58 @@ func handleMsgMhfPaymentAchievement(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfDisplayedAchievement(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfInfoScenarioCounter(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfInfoScenarioCounter(s *Session, p mhfpacket.MHFPacket) { + + pkt := p.(*mhfpacket.MsgMhfInfoScenarioCounter) + + scenarioCounter := []struct { + Unk0 uint32 // Main ID? + Unk1 uint8 + Unk2 uint8 + }{ + { + Unk0: 0x00000000, + Unk1: 1, + Unk2: 4, + }, + { + Unk0: 0x00000001, + Unk1: 1, + Unk2: 4, + }, + { + Unk0: 0x00000002, + Unk1: 1, + Unk2: 4, + }, + { + Unk0: 0x00000003, + Unk1: 1, + Unk2: 4, + }, + } + + resp := byteframe.NewByteFrame() + resp.WriteUint8(uint8(len(scenarioCounter))) // Entry count + for _, entry := range scenarioCounter { + resp.WriteUint32(entry.Unk0) + resp.WriteUint8(entry.Unk1) + resp.WriteUint8(entry.Unk2) + } + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) + + // DEBUG, DELETE ME! + /* + data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, "debug/info_scenario_counter_resp.bin")) + if err != nil { + panic(err) + } + + doSizedAckResp(s, pkt.AckHandle, data) + */ + +} func handleMsgMhfSaveScenarioData(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfSaveScenarioData) From fff92b16ed4da89baea6db6fd04cf0fffe3c8790 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Sat, 22 Feb 2020 11:20:52 -0500 Subject: [PATCH 44/49] Add bruteforce fallback for out-of-sync crypto --- network/crypt_conn.go | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/network/crypt_conn.go b/network/crypt_conn.go index 7cce26d37..6aeedad8e 100644 --- a/network/crypt_conn.go +++ b/network/crypt_conn.go @@ -1,6 +1,7 @@ package network import ( + "encoding/hex" "errors" "fmt" "io" @@ -16,17 +17,16 @@ type CryptConn struct { readKeyRot uint32 sendKeyRot uint32 sentPackets int32 + prevRecvPacketCombinedCheck uint16 prevSendPacketCombinedCheck uint16 } // NewCryptConn creates a new CryptConn with proper default values. func NewCryptConn(conn net.Conn) *CryptConn { cc := &CryptConn{ - conn: conn, - readKeyRot: 995117, - sendKeyRot: 995117, - sentPackets: 0, - prevSendPacketCombinedCheck: 0, + conn: conn, + readKeyRot: 995117, + sendKeyRot: 995117, } return cc } @@ -41,8 +41,6 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) { return nil, err } - //fmt.Printf("Header: %s\n", hex.Dump(headerData)) - // Parse the data into a usable struct. cph, err := NewCryptPacketHeader(headerData) if err != nil { @@ -65,10 +63,29 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) { if cph.Check0 != check0 || cph.Check1 != check1 || cph.Check2 != check2 { fmt.Printf("got c0 %X, c1 %X, c2 %X\n", check0, check1, check2) fmt.Printf("want c0 %X, c1 %X, c2 %X\n", cph.Check0, cph.Check1, cph.Check2) + fmt.Printf("headerData:\n%s\n", hex.Dump(headerData)) + fmt.Printf("encryptedPacketBody:\n%s\n", hex.Dump(encryptedPacketBody)) + + // Attempt to bruteforce it. + fmt.Println("Crypto out of sync? Attempting bruteforce") + for key := byte(0); key < 255; key++ { + out, combinedCheck, check0, check1, check2 = crypto.Decrypt(encryptedPacketBody, 0, &key) + //fmt.Printf("Key: 0x%X\n%s\n", key, hex.Dump(out)) + if cph.Check0 == check0 && cph.Check1 == check1 && cph.Check2 == check2 { + fmt.Printf("Bruceforce successful, override key: 0x%X\n", key) + + // Try to fix key for subsequent packets? + //cc.readKeyRot = (uint32(key) << 1) + 999983 + + cc.prevRecvPacketCombinedCheck = combinedCheck + return out, nil + } + } + return nil, errors.New("decrypted data checksum doesn't match header") } - _ = combinedCheck + cc.prevRecvPacketCombinedCheck = combinedCheck return out, nil } From ecc0e4949786024c8a82cdbdaa4b5a9cbe4b8aab Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Sat, 22 Feb 2020 21:03:35 -0500 Subject: [PATCH 45/49] Fix major crypto bug `cryptKeyTruncByte` was incorrectly typed as a byte when 1 was added. This caused `cryptKeyTruncByte` to wrap around to 0 when it was 0xFF, resulting in an incorrect multiplication by zero, breaking [en|de]cryption entirely. --- network/crypto/crypto.go | 2 +- network/crypto/crypto_test.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/network/crypto/crypto.go b/network/crypto/crypto.go index 6d4c9e662..87746fa90 100644 --- a/network/crypto/crypto.go +++ b/network/crypto/crypto.go @@ -28,7 +28,7 @@ func _generalCrypt(data []byte, rotKey uint32, cryptType int, overrideByteKey *b cryptKeyTruncByte = *overrideByteKey } - derivedCryptKey := int32((uint32(len(data)) * uint32(cryptKeyTruncByte+1)) & 0xFFFFFFFF) + derivedCryptKey := int32((uint32(len(data)) * (uint32(cryptKeyTruncByte) + 1)) & 0xFFFFFFFF) sharedBufIdx := byte(1) accumulator0 := uint32(0) accumulator1 := uint32(0) diff --git a/network/crypto/crypto_test.go b/network/crypto/crypto_test.go index 83dc57d90..32ff7ee7c 100644 --- a/network/crypto/crypto_test.go +++ b/network/crypto/crypto_test.go @@ -53,6 +53,12 @@ var tests = []struct { []byte{0x46, 0xB5, 0xDC, 0xB2}, 0x2ADD, 0x09A6, 0x021E, 0x08FB3, }, + { + []byte{0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x12, 0x00, 0xDE, 0x00, 0x03, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x20, 0x18, 0x46, 0x00, 0x00, 0x80, 0x3F, 0xDC, 0xE4, 0x0A, 0x46, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x67, 0xD3, 0x5B, 0x00, 0x77, 0x01, 0x78, 0x00, 0x77, 0x01, 0x4F, 0x01, 0x5B, 0x6F, 0x76, 0xC5, 0x30, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xDD, 0x17, 0x46, 0x00, 0x00, 0x80, 0x3F, 0x9E, 0x11, 0x0C, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x37, 0x64, 0x00, 0x2C, 0x01, 0x64, 0x00, 0x2C, 0x01, 0x4F, 0x01, 0x5B, 0x6F, 0x76, 0xC5, 0x00, 0x10}, + 2000476, + []byte{0x2E, 0x52, 0x24, 0xE3, 0x05, 0x2B, 0xFC, 0x04, 0x0B, 0x26, 0x90, 0xEA, 0x61, 0xDB, 0x8D, 0x27, 0xCB, 0xB1, 0x69, 0xA1, 0x77, 0x80, 0x4A, 0xC2, 0xA0, 0xBD, 0x50, 0x54, 0xF5, 0xC2, 0x94, 0x66, 0xBB, 0xCE, 0x53, 0x29, 0xEE, 0xB4, 0xFA, 0xF6, 0x5F, 0x8D, 0x80, 0x3E, 0x5D, 0x5F, 0xB0, 0x53, 0xE6, 0x92, 0x17, 0x80, 0xE7, 0xED, 0xE7, 0xDC, 0x61, 0xF0, 0xCD, 0xE4, 0x41, 0x82, 0x21, 0xBA, 0x47, 0xAB, 0x58, 0xFF, 0x30, 0x76, 0x80, 0x2D, 0x38, 0xF4, 0xDF, 0x86, 0x8C, 0x6C, 0x8D, 0x33, 0x4C, 0x37, 0xA3, 0xDA, 0x01, 0x3C, 0x98, 0x66, 0x1F, 0xB9, 0xE2, 0xEA, 0xF0, 0x84, 0xE8, 0xAA, 0x00, 0x3D, 0x4A, 0xB6, 0xF2, 0x3D, 0x91, 0x58, 0x4B, 0x0B, 0xE2, 0xD5, 0xC7, 0x39, 0x4D, 0x59, 0xED, 0xC3, 0x61, 0x6F, 0x6E, 0x69, 0x9B, 0x3C}, + 0xCFF8, 0x086B, 0x3BAE, 0x4057, + }, } func TestEncrypt(t *testing.T) { From 172b5decf4a2136b737e9e202ed91bd38448868b Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Sat, 22 Feb 2020 22:17:10 -0500 Subject: [PATCH 46/49] Implement multiple packet parsers --- network/mhfpacket/msg_mhf_get_seibattle.go | 19 +++++++++++++++--- .../msg_mhf_get_ud_bonus_quest_info.go | 9 ++++++--- network/mhfpacket/msg_mhf_post_boost_time.go | 9 +++++++-- .../msg_mhf_post_boost_time_quest_return.go | 9 ++++++--- .../msg_mhf_update_use_trend_weapon_log.go | 13 +++++++++--- network/mhfpacket/msg_sys_back_stage.go | 9 ++++++--- network/mhfpacket/msg_sys_record_log.go | 20 ++++++++++++++++--- 7 files changed, 68 insertions(+), 20 deletions(-) diff --git a/network/mhfpacket/msg_mhf_get_seibattle.go b/network/mhfpacket/msg_mhf_get_seibattle.go index f0ab6ca82..9c6efe62f 100644 --- a/network/mhfpacket/msg_mhf_get_seibattle.go +++ b/network/mhfpacket/msg_mhf_get_seibattle.go @@ -6,7 +6,14 @@ import ( ) // MsgMhfGetSeibattle represents the MSG_MHF_GET_SEIBATTLE -type MsgMhfGetSeibattle struct{} +type MsgMhfGetSeibattle struct { + AckHandle uint32 + Unk0 uint8 + Unk1 uint8 + Unk2 uint32 // Some shared ID with MSG_SYS_RECORD_LOG, world ID? + Unk3 uint8 + Unk4 uint16 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetSeibattle) Opcode() network.PacketID { @@ -15,10 +22,16 @@ func (m *MsgMhfGetSeibattle) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetSeibattle) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint8() + m.Unk2 = bf.ReadUint32() + m.Unk3 = bf.ReadUint8() + m.Unk4 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetSeibattle) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_get_ud_bonus_quest_info.go b/network/mhfpacket/msg_mhf_get_ud_bonus_quest_info.go index 3f77e96ae..35f586522 100644 --- a/network/mhfpacket/msg_mhf_get_ud_bonus_quest_info.go +++ b/network/mhfpacket/msg_mhf_get_ud_bonus_quest_info.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfGetUdBonusQuestInfo represents the MSG_MHF_GET_UD_BONUS_QUEST_INFO -type MsgMhfGetUdBonusQuestInfo struct{} +type MsgMhfGetUdBonusQuestInfo struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfGetUdBonusQuestInfo) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfGetUdBonusQuestInfo) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfGetUdBonusQuestInfo) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfGetUdBonusQuestInfo) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_post_boost_time.go b/network/mhfpacket/msg_mhf_post_boost_time.go index 82ad78f7d..ad298c5e2 100644 --- a/network/mhfpacket/msg_mhf_post_boost_time.go +++ b/network/mhfpacket/msg_mhf_post_boost_time.go @@ -6,7 +6,10 @@ import ( ) // MsgMhfPostBoostTime represents the MSG_MHF_POST_BOOST_TIME -type MsgMhfPostBoostTime struct{} +type MsgMhfPostBoostTime struct{ + AckHandle uint32 + BoostTime uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfPostBoostTime) Opcode() network.PacketID { @@ -15,7 +18,9 @@ func (m *MsgMhfPostBoostTime) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPostBoostTime) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.BoostTime = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_mhf_post_boost_time_quest_return.go b/network/mhfpacket/msg_mhf_post_boost_time_quest_return.go index 1cac00928..566caa60e 100644 --- a/network/mhfpacket/msg_mhf_post_boost_time_quest_return.go +++ b/network/mhfpacket/msg_mhf_post_boost_time_quest_return.go @@ -6,7 +6,9 @@ import ( ) // MsgMhfPostBoostTimeQuestReturn represents the MSG_MHF_POST_BOOST_TIME_QUEST_RETURN -type MsgMhfPostBoostTimeQuestReturn struct{} +type MsgMhfPostBoostTimeQuestReturn struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfPostBoostTimeQuestReturn) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgMhfPostBoostTimeQuestReturn) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfPostBoostTimeQuestReturn) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfPostBoostTimeQuestReturn) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_mhf_update_use_trend_weapon_log.go b/network/mhfpacket/msg_mhf_update_use_trend_weapon_log.go index 35bc9d1ca..be42fa1ba 100644 --- a/network/mhfpacket/msg_mhf_update_use_trend_weapon_log.go +++ b/network/mhfpacket/msg_mhf_update_use_trend_weapon_log.go @@ -6,7 +6,11 @@ import ( ) // MsgMhfUpdateUseTrendWeaponLog represents the MSG_MHF_UPDATE_USE_TREND_WEAPON_LOG -type MsgMhfUpdateUseTrendWeaponLog struct{} +type MsgMhfUpdateUseTrendWeaponLog struct { + AckHandle uint32 + Unk0 uint8 + Unk1 uint16 // Weapon/item ID probably? +} // Opcode returns the ID associated with this packet type. func (m *MsgMhfUpdateUseTrendWeaponLog) Opcode() network.PacketID { @@ -15,10 +19,13 @@ func (m *MsgMhfUpdateUseTrendWeaponLog) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfUpdateUseTrendWeaponLog) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint16() + return nil } // Build builds a binary packet from the current data. func (m *MsgMhfUpdateUseTrendWeaponLog) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_back_stage.go b/network/mhfpacket/msg_sys_back_stage.go index 3473f46da..1d435e7f6 100644 --- a/network/mhfpacket/msg_sys_back_stage.go +++ b/network/mhfpacket/msg_sys_back_stage.go @@ -6,7 +6,9 @@ import ( ) // MsgSysBackStage represents the MSG_SYS_BACK_STAGE -type MsgSysBackStage struct{} +type MsgSysBackStage struct { + AckHandle uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysBackStage) Opcode() network.PacketID { @@ -15,10 +17,11 @@ func (m *MsgSysBackStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysBackStage) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + return nil } // Build builds a binary packet from the current data. func (m *MsgSysBackStage) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} diff --git a/network/mhfpacket/msg_sys_record_log.go b/network/mhfpacket/msg_sys_record_log.go index 711a21d7d..2f72e30da 100644 --- a/network/mhfpacket/msg_sys_record_log.go +++ b/network/mhfpacket/msg_sys_record_log.go @@ -6,7 +6,14 @@ import ( ) // MsgSysRecordLog represents the MSG_SYS_RECORD_LOG -type MsgSysRecordLog struct{} +type MsgSysRecordLog struct { + AckHandle uint32 + Unk0 uint32 + Unk1 uint16 // Hardcoded 0 + HardcodedDataSize uint16 // Hardcoded 0x4AC + Unk3 uint32 // Some shared ID with MSG_MHF_GET_SEIBATTLE. World ID?? + DataBuf []byte +} // Opcode returns the ID associated with this packet type. func (m *MsgSysRecordLog) Opcode() network.PacketID { @@ -15,10 +22,17 @@ func (m *MsgSysRecordLog) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysRecordLog) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint32() + m.Unk1 = bf.ReadUint16() + m.HardcodedDataSize = bf.ReadUint16() + m.Unk3 = bf.ReadUint32() + m.DataBuf = bf.ReadBytes(uint(m.HardcodedDataSize)) + return nil + } // Build builds a binary packet from the current data. func (m *MsgSysRecordLog) Build(bf *byteframe.ByteFrame) error { panic("Not implemented") -} \ No newline at end of file +} From c50568689375eb1909e23bfd4fc4b419debcfd81 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Sat, 22 Feb 2020 22:18:16 -0500 Subject: [PATCH 47/49] Document charID-based house stageID --- server/channelserver/channel_server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/channelserver/channel_server.go b/server/channelserver/channel_server.go index 56c52b2cc..500a6bc3f 100644 --- a/server/channelserver/channel_server.go +++ b/server/channelserver/channel_server.go @@ -70,6 +70,8 @@ func NewServer(config *Config) *Server { stage3 := NewStage("sl2Ns379p0a0u0") s.stages[stage3.id] = stage3 + // sl1Ns257p0a0uE31111 -- house for charID E31111. + return s } From 1d8eec2280e7e4b3881afdcb8363a93e67b16c97 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Sat, 22 Feb 2020 22:20:29 -0500 Subject: [PATCH 48/49] Implement handlers for stage movement and quest completion --- common/stringstack/stringstack.go | 40 +++++++++++++++ server/channelserver/handlers.go | 81 ++++++++++++++++++++++++++++--- server/channelserver/session.go | 15 ++++-- 3 files changed, 124 insertions(+), 12 deletions(-) create mode 100644 common/stringstack/stringstack.go diff --git a/common/stringstack/stringstack.go b/common/stringstack/stringstack.go new file mode 100644 index 000000000..aff33842e --- /dev/null +++ b/common/stringstack/stringstack.go @@ -0,0 +1,40 @@ +package stringstack + +import ( + "errors" + "sync" +) + +// StringStack is a basic LIFO "stack" for storing strings. +type StringStack struct { + sync.Mutex + stack []string +} + +// New creates a new instance of StringStack +func New() *StringStack { + return &StringStack{} +} + +// Push pushes a string onto the stack. +func (s *StringStack) Push(v string) { + s.Lock() + defer s.Unlock() + + s.stack = append(s.stack, v) +} + +// Pop pops a string from the stack. +func (s *StringStack) Pop() (string, error) { + s.Lock() + defer s.Unlock() + + if len(s.stack) == 0 { + return "", errors.New("no items on stack") + } + + x := s.stack[len(s.stack)-1] + s.stack = s.stack[:len(s.stack)-1] + + return x, nil +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 0b68c15a6..8dc7b26c6 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -318,7 +318,11 @@ func handleMsgSysIssueLogkey(s *Session, p mhfpacket.MHFPacket) { doSizedAckResp(s, pkt.AckHandle, resp.Data()) } -func handleMsgSysRecordLog(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysRecordLog(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysRecordLog) + resp := make([]byte, 8) // Unk resp. + s.QueueAck(pkt.AckHandle, resp) +} func handleMsgSysEcho(s *Session, p mhfpacket.MHFPacket) {} @@ -464,13 +468,39 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) { func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysEnterStage) + + // Push our current stage ID to the movement stack before entering another one. + s.Lock() + s.stageMoveStack.Push(s.stageID) + s.Unlock() + doStageTransfer(s, pkt.AckHandle, pkt.StageID) } -func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysBackStage) + + // Transfer back to the saved stage ID before the previous move or enter. + s.Lock() + backStage, err := s.stageMoveStack.Pop() + s.Unlock() + + if err != nil { + panic(err) + } + + doStageTransfer(s, pkt.AckHandle, backStage) + +} func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysMoveStage) + + // Push our current stage ID to the movement stack before entering another one. + s.Lock() + s.stageMoveStack.Push(s.stageID) + s.Unlock() + doStageTransfer(s, pkt.AckHandle, pkt.StageID) } @@ -1613,7 +1643,9 @@ func handleMsgMhfGetBoostTime(s *Session, p mhfpacket.MHFPacket) { updateRights(s) } -func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) { + //pkt := p.(*mhfpacket.MsgMhfPostBoostTime) +} func handleMsgMhfGetBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfGetBoostTimeLimit) @@ -1672,7 +1704,10 @@ func handleMsgMhfGetBoostRight(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfStartBoostTime(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfPostBoostTimeQuestReturn) + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgMhfGetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) {} @@ -1680,7 +1715,10 @@ func handleMsgMhfPlayBoxGacha(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfResetBoxGachaInfo(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetSeibattle(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetSeibattle(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetSeibattle) + stubGetNoResults(s, pkt.AckHandle) +} func handleMsgMhfPostSeibattle(s *Session, p mhfpacket.MHFPacket) {} @@ -1828,7 +1866,33 @@ func handleMsgMhfGetUdMyPoint(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetUdTotalPointInfo(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfGetUdBonusQuestInfo(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfGetUdBonusQuestInfo(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfGetUdBonusQuestInfo) + + udBonusQuestInfos := []struct { + Unk0 uint8 + Unk1 uint8 + StartTime uint32 // Unix timestamp (seconds) + EndTime uint32 // Unix timestamp (seconds) + Unk4 uint32 + Unk5 uint8 + Unk6 uint8 + }{} // Blank stub array. + + resp := byteframe.NewByteFrame() + resp.WriteUint8(uint8(len(udBonusQuestInfos))) + for _, q := range udBonusQuestInfos { + resp.WriteUint8(q.Unk0) + resp.WriteUint8(q.Unk1) + resp.WriteUint32(q.StartTime) + resp.WriteUint32(q.EndTime) + resp.WriteUint32(q.Unk4) + resp.WriteUint8(q.Unk5) + resp.WriteUint8(q.Unk6) + } + + doSizedAckResp(s, pkt.AckHandle, resp.Data()) +} func handleMsgMhfGetUdSelectedColorInfo(s *Session, p mhfpacket.MHFPacket) {} @@ -2086,7 +2150,10 @@ func handleMsgSysReserve18F(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetTrendWeapon(s *Session, p mhfpacket.MHFPacket) {} -func handleMsgMhfUpdateUseTrendWeaponLog(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgMhfUpdateUseTrendWeaponLog(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgMhfUpdateUseTrendWeaponLog) + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) +} func handleMsgSysReserve192(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/session.go b/server/channelserver/session.go index 1d6c62545..002d04c93 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -6,6 +6,7 @@ import ( "net" "sync" + "github.com/Andoryuuta/Erupe/common/stringstack" "github.com/Andoryuuta/Erupe/network" "github.com/Andoryuuta/Erupe/network/mhfpacket" "github.com/Andoryuuta/byteframe" @@ -25,16 +26,20 @@ type Session struct { stage *Stage charID uint32 logKey []byte + + // A stack containing the stage movement history (push on enter/move, pop on back) + stageMoveStack *stringstack.StringStack } // NewSession creates a new Session type. func NewSession(server *Server, conn net.Conn) *Session { s := &Session{ - logger: server.logger.Named(conn.RemoteAddr().String()), - server: server, - rawConn: conn, - cryptConn: network.NewCryptConn(conn), - sendPackets: make(chan []byte, 20), + logger: server.logger.Named(conn.RemoteAddr().String()), + server: server, + rawConn: conn, + cryptConn: network.NewCryptConn(conn), + sendPackets: make(chan []byte, 20), + stageMoveStack: stringstack.New(), } return s } From 35f34d31dbdadeeee67b7f3a86cb400f149a77c4 Mon Sep 17 00:00:00 2001 From: Andrew Gutekanst Date: Sat, 22 Feb 2020 22:40:11 -0500 Subject: [PATCH 49/49] Add dev options to config --- config.json | 6 ++++++ config/config.go | 12 ++++++++++-- main.go | 9 ++++++--- server/signserver/dsgn_resp.go | 11 +++++++++-- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/config.json b/config.json index 28d079a77..280857944 100644 --- a/config.json +++ b/config.json @@ -2,6 +2,12 @@ "host_ip": "127.0.0.1", "bin_path": "bin", + "devmode": false, + "devmodeoptions": { + "cleandb": false, + "maxlauncherhr": true + }, + "database": { "host": "localhost", "port": 5432, diff --git a/config/config.go b/config/config.go index 816a332e1..30a420708 100644 --- a/config/config.go +++ b/config/config.go @@ -9,9 +9,11 @@ import ( // Config holds the global server-wide config. type Config struct { - HostIP string `mapstructure:"host_ip"` + HostIP string `mapstructure:"host_ip"` BinPath string `mapstructure:"bin_path"` + DevMode bool + DevModeOptions DevModeOptions Database Database Launcher Launcher Sign Sign @@ -19,6 +21,12 @@ type Config struct { Entrance Entrance } +// DevModeOptions holds various debug/temporary options for use while developing Erupe. +type DevModeOptions struct { + CleanDB bool // Automatically wipes the DB on server reset. + MaxLauncherHR bool // Sets the HR returned in the launcher to HR9 so that you can join non-beginner worlds. +} + // Database holds the postgres database config. type Database struct { Host string @@ -30,7 +38,7 @@ type Database struct { // Launcher holds the launcher server config. type Launcher struct { - Port int + Port int UseOriginalLauncherFiles bool } diff --git a/main.go b/main.go index 3869b6489..8c3318382 100644 --- a/main.go +++ b/main.go @@ -59,9 +59,12 @@ func main() { } logger.Info("Connected to database") - logger.Info("Cleaning DB") - cleanDB(db) - logger.Info("Done cleaning DB") + // Clean the DB if the option is on. + if erupeConfig.DevMode && erupeConfig.DevModeOptions.CleanDB { + logger.Info("Cleaning DB") + cleanDB(db) + logger.Info("Done cleaning DB") + } // Now start our server(s). diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index c05737ab5..28816825f 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -54,8 +54,15 @@ func (s *Session) makeSignInResp(uid int) []byte { uint8PascalString(bf, "mhf-n.capcom.com.tw") for _, char := range chars { - bf.WriteUint32(char.ID) // character ID 469153291 - bf.WriteUint16(char.Exp) // Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999 + bf.WriteUint32(char.ID) // character ID 469153291 + + // Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999 + if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.MaxLauncherHR { + bf.WriteUint16(999) + } else { + bf.WriteUint16(char.Exp) + } + bf.WriteUint16(char.Weapon) // Weapon, 0-13. bf.WriteUint32(char.LastLogin) // Last login date, unix timestamp in seconds. bf.WriteBool(char.IsFemale) // Sex, 0=male, 1=female.