2023-03-27 22:02:13 +00:00
package shard
2023-03-17 21:27:47 +00:00
import (
"fmt"
"log"
"net"
"sync"
2023-06-22 06:53:38 +00:00
"github.com/CPunch/gopenfusion/config"
2023-03-22 05:30:58 +00:00
"github.com/CPunch/gopenfusion/core"
"github.com/CPunch/gopenfusion/core/db"
"github.com/CPunch/gopenfusion/core/protocol"
2023-06-22 06:53:38 +00:00
"github.com/CPunch/gopenfusion/core/redis"
2023-03-17 21:27:47 +00:00
)
2023-03-27 22:02:13 +00:00
type PacketHandler func ( peer * protocol . CNPeer , pkt protocol . Packet ) error
func stubbedPacket ( _ * protocol . CNPeer , _ protocol . Packet ) error { /* stubbed */ return nil }
2023-03-17 21:27:47 +00:00
type ShardServer struct {
listener net . Listener
port int
dbHndlr * db . DBHandler
2023-06-22 06:53:38 +00:00
redisHndlr * redis . RedisHandler
2023-03-17 21:27:47 +00:00
packetHandlers map [ uint32 ] PacketHandler
loginMetadataQueue sync . Map // [int64]*LoginMetadata w/ int64 = serialKey
2023-03-27 02:08:13 +00:00
peersLock sync . Mutex
peers sync . Map // [*protocol.CNPeer]core.Player
2023-03-17 21:27:47 +00:00
}
2023-06-22 06:53:38 +00:00
func NewShardServer ( dbHndlr * db . DBHandler , redisHndlr * redis . RedisHandler , port int ) ( * ShardServer , error ) {
2023-03-17 21:27:47 +00:00
listener , err := net . Listen ( "tcp" , fmt . Sprintf ( ":%d" , port ) )
if err != nil {
return nil , err
}
server := & ShardServer {
listener : listener ,
port : port ,
dbHndlr : dbHndlr ,
2023-06-22 06:53:38 +00:00
redisHndlr : redisHndlr ,
2023-03-17 21:27:47 +00:00
packetHandlers : make ( map [ uint32 ] PacketHandler ) ,
}
2023-03-18 21:40:20 +00:00
server . packetHandlers = map [ uint32 ] PacketHandler {
2023-03-22 22:07:16 +00:00
protocol . P_CL2FE_REQ_PC_ENTER : server . RequestEnter ,
protocol . P_CL2FE_REQ_PC_LOADING_COMPLETE : server . LoadingComplete ,
2023-03-18 21:40:20 +00:00
}
2023-03-17 21:27:47 +00:00
2023-06-22 06:53:38 +00:00
redisHndlr . RegisterShard ( redis . ShardMetadata {
IP : config . GetAnnounceIP ( ) ,
Port : port ,
} )
2023-03-17 21:27:47 +00:00
return server , nil
}
func ( server * ShardServer ) RegisterPacketHandler ( typeID uint32 , hndlr PacketHandler ) {
server . packetHandlers [ typeID ] = hndlr
}
func ( server * ShardServer ) Start ( ) {
2023-06-22 06:53:38 +00:00
log . Printf ( "Shard service hosted on %s:%d\n" , config . GetAnnounceIP ( ) , server . port )
2023-03-17 21:27:47 +00:00
for {
conn , err := server . listener . Accept ( )
if err != nil {
log . Println ( "Connection error: " , err )
return
}
2023-03-18 21:40:20 +00:00
client := protocol . NewCNPeer ( server , conn )
2023-03-17 21:27:47 +00:00
server . Connect ( client )
go client . Handler ( )
}
}
2023-03-27 22:02:13 +00:00
func ( server * ShardServer ) GetPort ( ) int {
return server . port
}
2023-03-18 21:40:20 +00:00
func ( server * ShardServer ) HandlePacket ( peer * protocol . CNPeer , typeID uint32 , pkt protocol . Packet ) error {
2023-03-17 21:27:47 +00:00
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
}
2023-03-18 21:40:20 +00:00
func ( server * ShardServer ) Disconnect ( peer * protocol . CNPeer ) {
2023-03-17 21:27:47 +00:00
log . Printf ( "Peer %p disconnected from SHARD\n" , peer )
server . peers . Delete ( peer )
}
2023-03-18 21:40:20 +00:00
func ( server * ShardServer ) Connect ( peer * protocol . CNPeer ) {
2023-03-17 21:27:47 +00:00
log . Printf ( "New peer %p connected to SHARD\n" , peer )
server . peers . Store ( peer , nil )
}
2023-03-27 02:08:13 +00:00
func ( server * ShardServer ) LoadPlayer ( peer * protocol . CNPeer ) * core . Player {
2023-03-22 22:07:16 +00:00
val , ok := server . peers . Load ( peer )
if ! ok {
return nil
}
plr , ok := val . ( * core . Player )
if ! ok {
return nil
}
return plr
}
2023-03-27 02:08:13 +00:00
// UpdatePlayer locks the peers map, and calls the provided callback. The returned new pointer will be stored, however if an error returns it will be passed back.
// Since it is UNSAFE to write to the returned pointer from LoadPlayer, this wrapper is for the cases that state in the player struct needs to be updated.
// TODO: maybe LoadPlayer should return a player by value instead?
// The pointers new and old may be the same if you are just updating struct fields.
func ( server * ShardServer ) UpdatePlayer ( peer * protocol . CNPeer , f func ( old * core . Player ) ( new * core . Player , err error ) ) error {
server . peersLock . Lock ( )
defer server . peersLock . Unlock ( )
// on fail, the player should not be stored
new , err := f ( server . LoadPlayer ( peer ) )
if err != nil {
return err
}
server . storePlayer ( peer , new )
return nil
}
func ( server * ShardServer ) storePlayer ( peer * protocol . CNPeer , player * core . Player ) {
2023-03-17 21:27:47 +00:00
server . peers . Store ( peer , player )
}
// Simple wrapper for server.peers.Range, if f returns false the iteration is stopped.
2023-03-22 05:30:58 +00:00
func ( server * ShardServer ) RangePeers ( f func ( peer * protocol . CNPeer , player * core . Player ) bool ) {
2023-03-17 21:27:47 +00:00
server . peers . Range ( func ( key , value any ) bool { // simple wrapper to cast the datatypes
2023-03-18 21:40:20 +00:00
peer , ok := key . ( * protocol . CNPeer )
2023-03-17 21:27:47 +00:00
if ! ok { // this should never happen
panic ( fmt . Errorf ( "ShardServer.peers has an invalid key: peers[%#v] = %#v" , key , value ) )
}
2023-03-22 05:30:58 +00:00
player , ok := value . ( * core . Player )
2023-03-17 21:27:47 +00:00
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 )
} )
}