Channel server refactoring

This commit is contained in:
Andrew Gutekanst
2019-12-26 22:23:59 +09:00
parent 319cfcb2f7
commit 519238f54d
3 changed files with 357 additions and 1 deletions

View File

@@ -0,0 +1,84 @@
package channelserver
import (
"database/sql"
"fmt"
"net"
"sync"
)
// Config struct allows configuring the server.
type Config struct {
DB *sql.DB
ListenAddr string
}
// Server is a MHF channel server.
type Server struct {
sync.Mutex
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.
}
// NewServer creates a new Server type.
func NewServer(config *Config) *Server {
s := &Server{
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)
if err != nil {
return err
}
s.listener = l
//defer l.Close()
go s.acceptClients()
go s.manageSessions()
return nil
}
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.acceptConns <- conn
}
}
func (s *Server) manageSessions() {
for {
select {
case newConn := <-s.acceptConns:
session := NewSession(s, newConn)
s.Lock()
s.sessions[newConn] = session
s.Unlock()
session.Start()
case delConn := <-s.deleteConns:
s.Lock()
delete(s.sessions, delConn)
s.Unlock()
}
}
}

261
channelserver/session.go Normal file
View File

@@ -0,0 +1,261 @@
package channelserver
import (
"encoding/hex"
"fmt"
"io/ioutil"
"net"
"sync"
"github.com/Andoryuuta/Erupe/network"
"github.com/Andoryuuta/byteframe"
)
// Session holds state for the channel server connection.
type Session struct {
sync.Mutex
server *Server
rawConn net.Conn
cryptConn *network.CryptConn
}
// NewSession creates a new Session type.
func NewSession(server *Server, conn net.Conn) *Session {
s := &Session{
server: server,
rawConn: conn,
cryptConn: network.NewCryptConn(conn),
}
return s
}
// Start starts the session packet read&handle loop.
func (s *Session) Start() {
go func() {
fmt.Println("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")
return
}
handlePacket(s.cryptConn, pkt)
}
}()
}
var loadDataCount int
var getPaperDataCount int
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)
}
}

13
main.go
View File

@@ -5,6 +5,7 @@ import (
"fmt"
"time"
"github.com/Andoryuuta/Erupe/channelserver"
"github.com/Andoryuuta/Erupe/signserver"
_ "github.com/lib/pq"
)
@@ -38,7 +39,17 @@ func main() {
})
go signServer.Listen()
go doChannelServer(":54001")
//go doChannelServer(":54001")
channelServer := channelserver.NewServer(
&channelserver.Config{
DB: db,
ListenAddr: ":54001",
})
err = channelServer.Start()
if err != nil {
panic(err)
}
for {
time.Sleep(1 * time.Second)