mirror of
https://github.com/CPunch/gopenfusion.git
synced 2024-11-14 03:50:05 +00:00
server: started ShardServer
- protocol: added GenSerialKey() which securely generates an EnterSerialKey - login server accepts a shard via LoginServer.AddShard() - login server will pass LoginMetaData to the selected shard via ShardServer.QueueLogin() - misc. refactoring
This commit is contained in:
parent
7a26ffdcf7
commit
1357de99aa
@ -8,6 +8,10 @@ var (
|
||||
AEQUIP_COUNT = 9
|
||||
AINVEN_COUNT = 50
|
||||
ABANK_COUNT = 119
|
||||
|
||||
LOGIN_PORT = 23000
|
||||
SHARD_PORT = 23001
|
||||
SHARD_IP = "127.0.0.1"
|
||||
)
|
||||
|
||||
func GetMaxHP(level int) int {
|
||||
|
@ -39,15 +39,14 @@ type Player struct {
|
||||
SkywayLocationFlag []byte
|
||||
FirstUseFlag []byte
|
||||
Quests []byte
|
||||
/* Appearance tbl */
|
||||
Body int `db:"Body"`
|
||||
EyeColor int `db:"EyeColor"`
|
||||
FaceStyle int `db:"FaceStyle"`
|
||||
Gender int `db:"Gender"`
|
||||
HairColor int `db:"HairColor"`
|
||||
HairStyle int `db:"HairStyle"`
|
||||
Height int `db:"Height"`
|
||||
SkinColor int `db:"SkinColor"`
|
||||
Body int /* Appearance tbl start */
|
||||
EyeColor int
|
||||
FaceStyle int
|
||||
Gender int
|
||||
HairColor int
|
||||
HairStyle int
|
||||
Height int
|
||||
SkinColor int /* Appearance tbl end */
|
||||
}
|
||||
|
||||
// returns PlayerID, error
|
||||
|
@ -20,10 +20,6 @@ type DBHandler struct {
|
||||
//go:embed migrations/new.sql
|
||||
var createDBQuery string
|
||||
|
||||
var (
|
||||
DefaultDB *DBHandler
|
||||
)
|
||||
|
||||
func OpenLiteDB(dbPath string) (*DBHandler, error) {
|
||||
sqliteFmt := fmt.Sprintf("%s", dbPath)
|
||||
|
||||
|
28
main.go
28
main.go
@ -1,14 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/CPunch/gopenfusion/config"
|
||||
"github.com/CPunch/gopenfusion/db"
|
||||
"github.com/CPunch/gopenfusion/server"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db.DefaultDB, _ = db.OpenLiteDB("test.db")
|
||||
db.DefaultDB.Setup()
|
||||
|
||||
server := server.NewLoginServer()
|
||||
server.Start()
|
||||
dbHndlr, err := db.OpenLiteDB("test.db")
|
||||
if err != nil {
|
||||
log.Panicf("failed to open db: %v", err)
|
||||
}
|
||||
dbHndlr.Setup()
|
||||
|
||||
loginServer, err := server.NewLoginServer(dbHndlr, config.LOGIN_PORT)
|
||||
if err != nil {
|
||||
log.Panicf("failed to create login server: %v", err)
|
||||
}
|
||||
|
||||
shardServer, err := server.NewShardServer(dbHndlr, config.SHARD_PORT)
|
||||
if err != nil {
|
||||
log.Panicf("failed to create shard server: %v", err)
|
||||
}
|
||||
|
||||
loginServer.AddShard(shardServer)
|
||||
go loginServer.Start()
|
||||
|
||||
shardServer.Start()
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
@ -58,3 +59,13 @@ func CreateNewKey(uTime uint64, iv1, iv2 uint64) []byte {
|
||||
binary.LittleEndian.PutUint64(buf, uint64(key))
|
||||
return buf
|
||||
}
|
||||
|
||||
func GenSerialKey() (int64, error) {
|
||||
tmp := [8]byte{}
|
||||
if _, err := rand.Read(tmp[:]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// convert to uint64 && return
|
||||
return int64(binary.LittleEndian.Uint64(tmp[:])), nil
|
||||
}
|
||||
|
8
server/join.go
Normal file
8
server/join.go
Normal file
@ -0,0 +1,8 @@
|
||||
package server
|
||||
|
||||
import "github.com/CPunch/gopenfusion/protocol"
|
||||
|
||||
func (server *ShardServer) RequestEnter(peer *Peer, pkt protocol.Packet) error {
|
||||
|
||||
return nil
|
||||
}
|
@ -79,10 +79,10 @@ func (server *LoginServer) Login(peer *Peer, pkt protocol.Packet) error {
|
||||
}
|
||||
|
||||
// attempt login
|
||||
account, err := db.DefaultDB.TryLogin(loginPkt.SzID, loginPkt.SzPassword)
|
||||
account, err := server.dbHndlr.TryLogin(loginPkt.SzID, loginPkt.SzPassword)
|
||||
if err == db.LoginErrorInvalidID {
|
||||
// this is the default behavior, auto create the account if the ID isn't in use
|
||||
account, err = db.DefaultDB.NewAccount(loginPkt.SzID, loginPkt.SzPassword)
|
||||
account, err = server.dbHndlr.NewAccount(loginPkt.SzID, loginPkt.SzPassword)
|
||||
if err != nil {
|
||||
// respond with a dummy login error packet so the client doesn't get softlocked
|
||||
SendError(LOGIN_DATABASE_ERROR)
|
||||
@ -99,7 +99,7 @@ func (server *LoginServer) Login(peer *Peer, pkt protocol.Packet) error {
|
||||
|
||||
// grab player data
|
||||
peer.AccountID = account.AccountID
|
||||
plrs, err := db.DefaultDB.GetPlayers(account.AccountID)
|
||||
plrs, err := server.dbHndlr.GetPlayers(account.AccountID)
|
||||
if err != nil {
|
||||
SendError(LOGIN_DATABASE_ERROR)
|
||||
return err
|
||||
@ -124,7 +124,7 @@ func (server *LoginServer) Login(peer *Peer, pkt protocol.Packet) error {
|
||||
IZ: int32(plr.ZCoordinate),
|
||||
}
|
||||
|
||||
AEquip, err := db.DefaultDB.GetPlayerInventorySlots(plr.PlayerID, 0, config.AEQUIP_COUNT-1)
|
||||
AEquip, err := server.dbHndlr.GetPlayerInventorySlots(plr.PlayerID, 0, config.AEQUIP_COUNT-1)
|
||||
if err != nil {
|
||||
SendError(LOGIN_DATABASE_ERROR)
|
||||
return err
|
||||
@ -158,7 +158,7 @@ func (server *LoginServer) SaveCharacterName(peer *Peer, pkt protocol.Packet) er
|
||||
}
|
||||
|
||||
// TODO: sanity check SzFirstName && SzLastName
|
||||
PlayerID, err := db.DefaultDB.NewPlayer(peer.AccountID, charPkt.SzFirstName, charPkt.SzLastName, int(charPkt.ISlotNum))
|
||||
PlayerID, err := server.dbHndlr.NewPlayer(peer.AccountID, charPkt.SzFirstName, charPkt.SzLastName, int(charPkt.ISlotNum))
|
||||
if err != nil {
|
||||
peer.Send(protocol.P_LS2CL_REP_SAVE_CHAR_NAME_FAIL, protocol.SP_LS2CL_REP_SAVE_CHAR_NAME_FAIL{})
|
||||
return err
|
||||
@ -218,11 +218,11 @@ func (server *LoginServer) CharacterCreate(peer *Peer, pkt protocol.Packet) erro
|
||||
return SendFail(peer)
|
||||
}
|
||||
|
||||
if err := db.DefaultDB.FinishPlayer(&charPkt, peer.AccountID); err != nil {
|
||||
if err := server.dbHndlr.FinishPlayer(&charPkt, peer.AccountID); err != nil {
|
||||
return SendFail(peer)
|
||||
}
|
||||
|
||||
plr, err := db.DefaultDB.GetPlayer(int(charPkt.PCStyle.IPC_UID))
|
||||
plr, err := server.dbHndlr.GetPlayer(int(charPkt.PCStyle.IPC_UID))
|
||||
if err != nil {
|
||||
return SendFail(peer)
|
||||
}
|
||||
@ -240,7 +240,7 @@ func (server *LoginServer) CharacterDelete(peer *Peer, pkt protocol.Packet) erro
|
||||
var charPkt protocol.SP_CL2LS_REQ_CHAR_DELETE
|
||||
pkt.Decode(&charPkt)
|
||||
|
||||
slot, err := db.DefaultDB.DeletePlayer(int(charPkt.IPC_UID), peer.AccountID)
|
||||
slot, err := server.dbHndlr.DeletePlayer(int(charPkt.IPC_UID), peer.AccountID)
|
||||
if err != nil {
|
||||
return SendFail(peer)
|
||||
}
|
||||
@ -250,11 +250,43 @@ func (server *LoginServer) CharacterDelete(peer *Peer, pkt protocol.Packet) erro
|
||||
})
|
||||
}
|
||||
|
||||
func (server *LoginServer) ShardSelect(peer *Peer, pkt protocol.Packet) error {
|
||||
var selection protocol.SP_CL2LS_REQ_CHAR_SELECT
|
||||
pkt.Decode(&selection)
|
||||
|
||||
if server.shard == nil {
|
||||
return fmt.Errorf("LoginServer currently has no linked shard")
|
||||
}
|
||||
|
||||
key, err := protocol.GenSerialKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: verify peer->AccountID and selection->IPC_UID are valid!!!!
|
||||
|
||||
resp := protocol.SP_LS2CL_REP_SHARD_SELECT_SUCC{
|
||||
G_FE_ServerPort: int32(server.shard.port),
|
||||
IEnterSerialKey: key,
|
||||
}
|
||||
|
||||
// the rest of the bytes in G_FE_ServerIP will be zero'd, so there's no need to write the NULL byte
|
||||
copy(resp.G_FE_ServerIP[:], []byte(config.SHARD_IP))
|
||||
|
||||
server.shard.QueueLogin(key, &LoginMetadata{
|
||||
FEKey: peer.FE_key,
|
||||
Timestamp: time.Now(),
|
||||
PlayerID: int32(selection.IPC_UID),
|
||||
})
|
||||
|
||||
return peer.Send(protocol.P_LS2CL_REP_SHARD_SELECT_SUCC, resp)
|
||||
}
|
||||
|
||||
func (server *LoginServer) FinishTutorial(peer *Peer, pkt protocol.Packet) error {
|
||||
var charPkt protocol.SP_CL2LS_REQ_SAVE_CHAR_TUTOR
|
||||
pkt.Decode(&charPkt)
|
||||
|
||||
if err := db.DefaultDB.FinishTutorial(int(charPkt.IPC_UID), peer.AccountID); err != nil {
|
||||
if err := server.dbHndlr.FinishTutorial(int(charPkt.IPC_UID), peer.AccountID); err != nil {
|
||||
return SendFail(peer)
|
||||
}
|
||||
|
||||
|
@ -1,61 +1,60 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/CPunch/gopenfusion/db"
|
||||
"github.com/CPunch/gopenfusion/protocol"
|
||||
)
|
||||
|
||||
type PacketHandler func(peer *Peer, pkt protocol.Packet) error
|
||||
|
||||
type LoginServer struct {
|
||||
listener net.Listener
|
||||
port int
|
||||
dbHndlr *db.DBHandler
|
||||
packetHandlers map[uint32]PacketHandler
|
||||
peers map[*Peer]bool
|
||||
peersLock sync.Mutex
|
||||
shard *ShardServer
|
||||
}
|
||||
|
||||
func stubbedPacket(_ *Peer, _ protocol.Packet) error { /* stubbed */ return nil }
|
||||
|
||||
func NewLoginServer() *LoginServer {
|
||||
listener, err := net.Listen("tcp", ":23000")
|
||||
func NewLoginServer(dbHndlr *db.DBHandler, port int) (*LoginServer, error) {
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
loginServer := &LoginServer{
|
||||
server := &LoginServer{
|
||||
listener: listener,
|
||||
port: port,
|
||||
dbHndlr: dbHndlr,
|
||||
peers: make(map[*Peer]bool),
|
||||
}
|
||||
|
||||
loginServer.packetHandlers = map[uint32]PacketHandler{
|
||||
protocol.P_CL2LS_REQ_LOGIN: loginServer.Login,
|
||||
protocol.P_CL2LS_REQ_CHECK_CHAR_NAME: loginServer.CheckCharacterName,
|
||||
protocol.P_CL2LS_REQ_SAVE_CHAR_NAME: loginServer.SaveCharacterName,
|
||||
protocol.P_CL2LS_REQ_CHAR_CREATE: loginServer.CharacterCreate,
|
||||
protocol.P_CL2LS_REQ_CHAR_SELECT: stubbedPacket,
|
||||
protocol.P_CL2LS_REQ_CHAR_DELETE: loginServer.CharacterDelete,
|
||||
server.packetHandlers = map[uint32]PacketHandler{
|
||||
protocol.P_CL2LS_REQ_LOGIN: server.Login,
|
||||
protocol.P_CL2LS_REQ_CHECK_CHAR_NAME: server.CheckCharacterName,
|
||||
protocol.P_CL2LS_REQ_SAVE_CHAR_NAME: server.SaveCharacterName,
|
||||
protocol.P_CL2LS_REQ_CHAR_CREATE: server.CharacterCreate,
|
||||
protocol.P_CL2LS_REQ_CHAR_SELECT: server.ShardSelect,
|
||||
protocol.P_CL2LS_REQ_CHAR_DELETE: server.CharacterDelete,
|
||||
protocol.P_CL2LS_REQ_SHARD_SELECT: stubbedPacket,
|
||||
protocol.P_CL2LS_REQ_SHARD_LIST_INFO: stubbedPacket,
|
||||
protocol.P_CL2LS_CHECK_NAME_LIST: stubbedPacket,
|
||||
protocol.P_CL2LS_REQ_SAVE_CHAR_TUTOR: loginServer.FinishTutorial,
|
||||
protocol.P_CL2LS_REQ_SAVE_CHAR_TUTOR: server.FinishTutorial,
|
||||
protocol.P_CL2LS_REQ_PC_EXIT_DUPLICATE: stubbedPacket,
|
||||
protocol.P_CL2LS_REP_LIVE_CHECK: stubbedPacket,
|
||||
protocol.P_CL2LS_REQ_CHANGE_CHAR_NAME: stubbedPacket,
|
||||
protocol.P_CL2LS_REQ_SERVER_SELECT: stubbedPacket,
|
||||
}
|
||||
|
||||
return loginServer
|
||||
}
|
||||
|
||||
func (server *LoginServer) RegisterPacketHandler(typeID uint32, hndlr PacketHandler) {
|
||||
server.packetHandlers[typeID] = hndlr
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func (server *LoginServer) Start() {
|
||||
log.Print("Server hosted on 127.0.0.1:23000")
|
||||
log.Printf("Server hosted on 127.0.0.1:%d\n", server.port)
|
||||
|
||||
for {
|
||||
conn, err := server.listener.Accept()
|
||||
@ -84,12 +83,18 @@ func (server *LoginServer) HandlePacket(peer *Peer, typeID uint32, pkt protocol.
|
||||
|
||||
func (server *LoginServer) Disconnect(peer *Peer) {
|
||||
server.peersLock.Lock()
|
||||
log.Printf("Peer %p disconnected from LOGIN\n", peer)
|
||||
delete(server.peers, peer)
|
||||
server.peersLock.Unlock()
|
||||
}
|
||||
|
||||
func (server *LoginServer) Connect(peer *Peer) {
|
||||
server.peersLock.Lock()
|
||||
log.Printf("New peer %p connected to LOGIN\n", peer)
|
||||
server.peers[peer] = true
|
||||
server.peersLock.Unlock()
|
||||
}
|
||||
|
||||
func (server *LoginServer) AddShard(shard *ShardServer) {
|
||||
server.shard = shard
|
||||
}
|
@ -7,7 +7,6 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/CPunch/gopenfusion/db"
|
||||
"github.com/CPunch/gopenfusion/protocol"
|
||||
"github.com/CPunch/gopenfusion/protocol/pool"
|
||||
)
|
||||
@ -24,7 +23,6 @@ type PeerHandler interface {
|
||||
}
|
||||
|
||||
type Peer struct {
|
||||
Player *db.Player
|
||||
conn net.Conn
|
||||
handler PeerHandler
|
||||
SzID string
|
||||
@ -37,15 +35,14 @@ type Peer struct {
|
||||
|
||||
func NewPeer(handler PeerHandler, conn net.Conn) *Peer {
|
||||
return &Peer{
|
||||
conn: conn,
|
||||
handler: handler,
|
||||
SzID: "",
|
||||
E_key: []byte(protocol.DEFAULT_KEY),
|
||||
FE_key: nil,
|
||||
SzID: "",
|
||||
AccountID: -1,
|
||||
Player: nil,
|
||||
handler: handler,
|
||||
conn: conn,
|
||||
alive: true,
|
||||
whichKey: USE_E,
|
||||
alive: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
126
server/shardServer.go
Normal file
126
server/shardServer.go
Normal file
@ -0,0 +1,126 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/CPunch/gopenfusion/db"
|
||||
"github.com/CPunch/gopenfusion/protocol"
|
||||
)
|
||||
|
||||
type LoginMetadata struct {
|
||||
FEKey []byte
|
||||
Timestamp time.Time
|
||||
PlayerID int32
|
||||
}
|
||||
|
||||
type ShardServer struct {
|
||||
listener net.Listener
|
||||
port int
|
||||
dbHndlr *db.DBHandler
|
||||
packetHandlers map[uint32]PacketHandler
|
||||
peers sync.Map // [*Peer]*db.Player
|
||||
loginMetadataQueue sync.Map // [int64]*LoginMetadata w/ int64 = serialKey
|
||||
}
|
||||
|
||||
func NewShardServer(dbHndlr *db.DBHandler, port int) (*ShardServer, error) {
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
server := &ShardServer{
|
||||
listener: listener,
|
||||
port: port,
|
||||
dbHndlr: dbHndlr,
|
||||
packetHandlers: make(map[uint32]PacketHandler),
|
||||
}
|
||||
|
||||
server.packetHandlers = map[uint32]PacketHandler{}
|
||||
|
||||
return server, nil
|
||||
}
|
||||
|
||||
func (server *ShardServer) RegisterPacketHandler(typeID uint32, hndlr PacketHandler) {
|
||||
server.packetHandlers[typeID] = hndlr
|
||||
}
|
||||
|
||||
func (server *ShardServer) Start() {
|
||||
log.Printf("Server hosted on 127.0.0.1:%d\n", server.port)
|
||||
|
||||
for {
|
||||
conn, err := server.listener.Accept()
|
||||
if err != nil {
|
||||
log.Println("Connection error: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
client := NewPeer(server, conn)
|
||||
server.Connect(client)
|
||||
go client.Handler()
|
||||
}
|
||||
}
|
||||
|
||||
func (server *ShardServer) HandlePacket(peer *Peer, typeID uint32, pkt protocol.Packet) error {
|
||||
if hndlr, ok := server.packetHandlers[typeID]; ok {
|
||||
if err := hndlr(peer, pkt); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Printf("[WARN] invalid packet ID: %x\n", typeID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (server *ShardServer) Disconnect(peer *Peer) {
|
||||
log.Printf("Peer %p disconnected from SHARD\n", peer)
|
||||
server.peers.Delete(peer)
|
||||
}
|
||||
|
||||
func (server *ShardServer) Connect(peer *Peer) {
|
||||
log.Printf("New peer %p connected to SHARD\n", peer)
|
||||
server.peers.Store(peer, nil)
|
||||
}
|
||||
|
||||
func (server *ShardServer) JoinPlayer(peer *Peer, player *db.Player) {
|
||||
server.peers.Store(peer, player)
|
||||
}
|
||||
|
||||
// Simple wrapper for server.peers.Range, if f returns false the iteration is stopped.
|
||||
func (server *ShardServer) RangePeers(f func(peer *Peer, player *db.Player) bool) {
|
||||
server.peers.Range(func(key, value any) bool { // simple wrapper to cast the datatypes
|
||||
peer, ok := key.(*Peer)
|
||||
if !ok { // this should never happen
|
||||
panic(fmt.Errorf("ShardServer.peers has an invalid key: peers[%#v] = %#v", key, value))
|
||||
}
|
||||
|
||||
player, ok := value.(*db.Player)
|
||||
if !ok { // this should also never happen
|
||||
panic(fmt.Errorf("ShardServer.peers has an invalid value: peers[%#v] = %#v", key, value))
|
||||
}
|
||||
|
||||
return f(peer, player)
|
||||
})
|
||||
}
|
||||
|
||||
func (server *ShardServer) QueueLogin(serialKey int64, meta *LoginMetadata) {
|
||||
server.loginMetadataQueue.Store(serialKey, meta)
|
||||
}
|
||||
|
||||
func (server *ShardServer) CheckLogin(serialKey int64) (*LoginMetadata, error) {
|
||||
value, ok := server.loginMetadataQueue.Load(serialKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("serialKey %x is not valid!", serialKey)
|
||||
}
|
||||
|
||||
meta, ok := value.(*LoginMetadata)
|
||||
if !ok { // should never happen
|
||||
panic(fmt.Errorf("ShardServer.loginMetadataQueue has an invalid value: loginMetadataQueue[%x] = %#v", serialKey, value))
|
||||
}
|
||||
|
||||
return meta, nil
|
||||
}
|
9
server/shared.go
Normal file
9
server/shared.go
Normal file
@ -0,0 +1,9 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/CPunch/gopenfusion/protocol"
|
||||
)
|
||||
|
||||
type PacketHandler func(peer *Peer, pkt protocol.Packet) error
|
||||
|
||||
func stubbedPacket(_ *Peer, _ protocol.Packet) error { /* stubbed */ return nil }
|
Loading…
Reference in New Issue
Block a user