mirror of
https://github.com/CPunch/gopenfusion.git
synced 2024-12-04 22:46:32 +00:00
Compare commits
13 Commits
96130ddc8d
...
899b95b4e6
Author | SHA1 | Date | |
---|---|---|---|
899b95b4e6 | |||
e33b7c0556 | |||
3445b852fd | |||
557117f093 | |||
b07e9ddbcb | |||
af867ccff2 | |||
c60017f78f | |||
e1804a1042 | |||
bcc999db38 | |||
e355af19ab | |||
0ed19ad6c5 | |||
66fe3c9738 | |||
72dbfe2541 |
2
.github/workflows/tests.yaml
vendored
2
.github/workflows/tests.yaml
vendored
@ -23,4 +23,4 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
go-version: '1.21.x'
|
go-version: '1.21.x'
|
||||||
- name: Test with the Go CLI
|
- name: Test with the Go CLI
|
||||||
run: go test -v ./...
|
run: go test -timeout 10s -v ./...
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package protocol
|
package cnet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -8,7 +8,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
|
||||||
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -22,8 +23,8 @@ type PacketEvent struct {
|
|||||||
PktID uint32
|
PktID uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// CNPeer is a simple wrapper for net.Conn connections to send/recv packets over the Fusionfall packet protocol.
|
// Peer is a simple wrapper for net.Conn connections to send/recv packets over the Fusionfall packet protocol.
|
||||||
type CNPeer struct {
|
type Peer struct {
|
||||||
uData interface{}
|
uData interface{}
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@ -37,42 +38,38 @@ type CNPeer struct {
|
|||||||
FE_key []byte
|
FE_key []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTime() uint64 {
|
func NewPeer(ctx context.Context, conn net.Conn) *Peer {
|
||||||
return uint64(time.Now().UnixMilli())
|
p := &Peer{
|
||||||
}
|
|
||||||
|
|
||||||
func NewCNPeer(ctx context.Context, conn net.Conn) *CNPeer {
|
|
||||||
p := &CNPeer{
|
|
||||||
conn: conn,
|
conn: conn,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
whichKey: USE_E,
|
whichKey: USE_E,
|
||||||
alive: &atomic.Bool{},
|
alive: &atomic.Bool{},
|
||||||
|
|
||||||
E_key: []byte(DEFAULT_KEY),
|
E_key: []byte(protocol.DEFAULT_KEY),
|
||||||
FE_key: nil,
|
FE_key: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (peer *CNPeer) SetUserData(uData interface{}) {
|
func (peer *Peer) SetUserData(uData interface{}) {
|
||||||
peer.uData = uData
|
peer.uData = uData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (peer *CNPeer) UserData() interface{} {
|
func (peer *Peer) UserData() interface{} {
|
||||||
return peer.uData
|
return peer.uData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (peer *CNPeer) Send(typeID uint32, data ...interface{}) error {
|
func (peer *Peer) Send(typeID uint32, data ...interface{}) error {
|
||||||
// grab buffer from pool
|
// grab buffer from pool
|
||||||
buf := GetBuffer()
|
buf := protocol.GetBuffer()
|
||||||
defer PutBuffer(buf)
|
defer protocol.PutBuffer(buf)
|
||||||
|
|
||||||
// allocate space for packet size
|
// allocate space for packet size
|
||||||
buf.Write(make([]byte, 4))
|
buf.Write(make([]byte, 4))
|
||||||
|
|
||||||
// body start
|
// body start
|
||||||
pkt := NewPacket(buf)
|
pkt := protocol.NewPacket(buf)
|
||||||
|
|
||||||
// encode type id
|
// encode type id
|
||||||
if err := pkt.Encode(typeID); err != nil {
|
if err := pkt.Encode(typeID); err != nil {
|
||||||
@ -97,7 +94,7 @@ func (peer *CNPeer) Send(typeID uint32, data ...interface{}) error {
|
|||||||
case USE_FE:
|
case USE_FE:
|
||||||
key = peer.FE_key
|
key = peer.FE_key
|
||||||
}
|
}
|
||||||
EncryptData(buf.Bytes()[4:], key)
|
protocol.EncryptData(buf.Bytes()[4:], key)
|
||||||
|
|
||||||
// send full packet
|
// send full packet
|
||||||
// log.Printf("Sending %#v, sizeof: %d, buffer: %v", data, buf.Len(), buf.Bytes())
|
// log.Printf("Sending %#v, sizeof: %d, buffer: %v", data, buf.Len(), buf.Bytes())
|
||||||
@ -107,11 +104,11 @@ func (peer *CNPeer) Send(typeID uint32, data ...interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (peer *CNPeer) SetActiveKey(whichKey int) {
|
func (peer *Peer) SetActiveKey(whichKey int) {
|
||||||
peer.whichKey = whichKey
|
peer.whichKey = whichKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func (peer *CNPeer) Kill() {
|
func (peer *Peer) Kill() {
|
||||||
// de-bounce: only kill if alive
|
// de-bounce: only kill if alive
|
||||||
if !peer.alive.CompareAndSwap(true, false) {
|
if !peer.alive.CompareAndSwap(true, false) {
|
||||||
return
|
return
|
||||||
@ -121,7 +118,7 @@ func (peer *CNPeer) Kill() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// meant to be invoked as a goroutine
|
// meant to be invoked as a goroutine
|
||||||
func (peer *CNPeer) Handler(eRecv chan<- *PacketEvent) error {
|
func (peer *Peer) Handler(eRecv chan<- *PacketEvent) error {
|
||||||
defer func() {
|
defer func() {
|
||||||
close(eRecv)
|
close(eRecv)
|
||||||
peer.Kill()
|
peer.Kill()
|
||||||
@ -140,19 +137,19 @@ func (peer *CNPeer) Handler(eRecv chan<- *PacketEvent) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// client should never send a packet size outside of this range
|
// client should never send a packet size outside of this range
|
||||||
if sz > CN_PACKET_BUFFER_SIZE || sz < 4 {
|
if sz > protocol.CN_PACKET_BUFFER_SIZE || sz < 4 {
|
||||||
return fmt.Errorf("invalid packet size: %d", sz)
|
return fmt.Errorf("invalid packet size: %d", sz)
|
||||||
}
|
}
|
||||||
|
|
||||||
// grab buffer && read packet body
|
// grab buffer && read packet body
|
||||||
buf := GetBuffer()
|
buf := protocol.GetBuffer()
|
||||||
if _, err := buf.ReadFrom(io.LimitReader(peer.conn, int64(sz))); err != nil {
|
if _, err := buf.ReadFrom(io.LimitReader(peer.conn, int64(sz))); err != nil {
|
||||||
return fmt.Errorf("failed to read packet body: %v", err)
|
return fmt.Errorf("failed to read packet body: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// decrypt
|
// decrypt
|
||||||
DecryptData(buf.Bytes(), peer.E_key)
|
protocol.DecryptData(buf.Bytes(), peer.E_key)
|
||||||
pkt := NewPacket(buf)
|
pkt := protocol.NewPacket(buf)
|
||||||
|
|
||||||
// create packet && read pktID
|
// create packet && read pktID
|
||||||
var pktID uint32
|
var pktID uint32
|
@ -4,7 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
"github.com/matryer/is"
|
"github.com/matryer/is"
|
||||||
)
|
)
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
package service
|
package cnet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -10,13 +10,13 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
"github.com/CPunch/gopenfusion/config"
|
"github.com/CPunch/gopenfusion/config"
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PacketHandler func(peer *protocol.CNPeer, pkt protocol.Packet) error
|
type PacketHandler func(peer *Peer, pkt protocol.Packet) error
|
||||||
|
|
||||||
func StubbedPacket(_ *protocol.CNPeer, _ protocol.Packet) error {
|
func StubbedPacket(_ *Peer, _ protocol.Packet) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,18 +28,18 @@ type Service struct {
|
|||||||
started chan struct{}
|
started chan struct{}
|
||||||
stopped chan struct{}
|
stopped chan struct{}
|
||||||
packetHandlers map[uint32]PacketHandler
|
packetHandlers map[uint32]PacketHandler
|
||||||
peers map[chan *protocol.PacketEvent]*protocol.CNPeer
|
peers map[chan *PacketEvent]*Peer
|
||||||
stateLock sync.Mutex
|
stateLock sync.Mutex
|
||||||
|
|
||||||
// OnDisconnect is called when a peer disconnects from the service.
|
// OnDisconnect is called when a peer disconnects from the service.
|
||||||
// uData is the stored value of the key/value pair in the peer map.
|
// uData is the stored value of the key/value pair in the peer map.
|
||||||
// It may not be set while the service is running. (eg. srvc.Start() has been called)
|
// It may not be set while the service is running. (eg. srvc.Start() has been called)
|
||||||
OnDisconnect func(peer *protocol.CNPeer)
|
OnDisconnect func(peer *Peer)
|
||||||
|
|
||||||
// OnConnect is called when a peer connects to the service.
|
// OnConnect is called when a peer connects to the service.
|
||||||
// return value is used as the value in the peer map.
|
// return value is used as the value in the peer map.
|
||||||
// It may not be set while the service is running. (eg. srvc.Start() has been called)
|
// It may not be set while the service is running. (eg. srvc.Start() has been called)
|
||||||
OnConnect func(peer *protocol.CNPeer)
|
OnConnect func(peer *Peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RandomPort() (int, error) {
|
func RandomPort() (int, error) {
|
||||||
@ -69,7 +69,7 @@ func NewService(ctx context.Context, name string, port int) *Service {
|
|||||||
func (srvc *Service) Reset(ctx context.Context) {
|
func (srvc *Service) Reset(ctx context.Context) {
|
||||||
srvc.ctx = ctx
|
srvc.ctx = ctx
|
||||||
srvc.packetHandlers = make(map[uint32]PacketHandler)
|
srvc.packetHandlers = make(map[uint32]PacketHandler)
|
||||||
srvc.peers = make(map[chan *protocol.PacketEvent]*protocol.CNPeer)
|
srvc.peers = make(map[chan *PacketEvent]*Peer)
|
||||||
srvc.started = make(chan struct{})
|
srvc.started = make(chan struct{})
|
||||||
srvc.stopped = make(chan struct{})
|
srvc.stopped = make(chan struct{})
|
||||||
}
|
}
|
||||||
@ -80,8 +80,8 @@ func (srvc *Service) AddPacketHandler(pktID uint32, handler PacketHandler) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type newPeerConnection struct {
|
type newPeerConnection struct {
|
||||||
peer *protocol.CNPeer
|
peer *Peer
|
||||||
channel chan *protocol.PacketEvent
|
channel chan *PacketEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srvc *Service) Start() error {
|
func (srvc *Service) Start() error {
|
||||||
@ -112,22 +112,22 @@ func (srvc *Service) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create a new peer and pass it to the event loop
|
// create a new peer and pass it to the event loop
|
||||||
peer := protocol.NewCNPeer(srvc.ctx, conn)
|
peer := NewPeer(srvc.ctx, conn)
|
||||||
eRecv := make(chan *protocol.PacketEvent)
|
eRecv := make(chan *PacketEvent)
|
||||||
peerConnections <- newPeerConnection{channel: eRecv, peer: peer}
|
peerConnections <- newPeerConnection{channel: eRecv, peer: peer}
|
||||||
go peer.Handler(eRecv)
|
go peer.Handler(eRecv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srvc *Service) getPeer(channel chan *protocol.PacketEvent) *protocol.CNPeer {
|
func (srvc *Service) getPeer(channel chan *PacketEvent) *Peer {
|
||||||
return srvc.peers[channel]
|
return srvc.peers[channel]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srvc *Service) setPeer(channel chan *protocol.PacketEvent, peer *protocol.CNPeer) {
|
func (srvc *Service) setPeer(channel chan *PacketEvent, peer *Peer) {
|
||||||
srvc.peers[channel] = peer
|
srvc.peers[channel] = peer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srvc *Service) removePeer(channel chan *protocol.PacketEvent) {
|
func (srvc *Service) removePeer(channel chan *PacketEvent) {
|
||||||
delete(srvc.peers, channel)
|
delete(srvc.peers, channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ func (srvc *Service) Stopped() <-chan struct{} {
|
|||||||
// if f returns false, the iteration is stopped.
|
// if f returns false, the iteration is stopped.
|
||||||
// NOTE: the peer map is not locked while iterating, if you're calling this
|
// NOTE: the peer map is not locked while iterating, if you're calling this
|
||||||
// outside of the service's event loop, you'll need to lock the peer map yourself.
|
// outside of the service's event loop, you'll need to lock the peer map yourself.
|
||||||
func (srvc *Service) RangePeers(f func(peer *protocol.CNPeer) bool) {
|
func (srvc *Service) RangePeers(f func(peer *Peer) bool) {
|
||||||
for _, peer := range srvc.peers {
|
for _, peer := range srvc.peers {
|
||||||
if !f(peer) {
|
if !f(peer) {
|
||||||
break
|
break
|
||||||
@ -167,7 +167,7 @@ func (srvc *Service) Unlock() {
|
|||||||
|
|
||||||
func (srvc *Service) stop() {
|
func (srvc *Service) stop() {
|
||||||
// OnDisconnect handler might need to do something important
|
// OnDisconnect handler might need to do something important
|
||||||
srvc.RangePeers(func(peer *protocol.CNPeer) bool {
|
srvc.RangePeers(func(peer *Peer) bool {
|
||||||
peer.Kill()
|
peer.Kill()
|
||||||
if srvc.OnDisconnect != nil {
|
if srvc.OnDisconnect != nil {
|
||||||
srvc.OnDisconnect(peer)
|
srvc.OnDisconnect(peer)
|
||||||
@ -196,7 +196,7 @@ func (srvc *Service) handleEvents(peerPipe <-chan newPeerConnection) {
|
|||||||
Chan: reflect.ValueOf(peerPipe),
|
Chan: reflect.ValueOf(peerPipe),
|
||||||
})
|
})
|
||||||
|
|
||||||
addPoll := func(channel chan *protocol.PacketEvent) {
|
addPoll := func(channel chan *PacketEvent) {
|
||||||
poll = append(poll, reflect.SelectCase{
|
poll = append(poll, reflect.SelectCase{
|
||||||
Dir: reflect.SelectRecv,
|
Dir: reflect.SelectRecv,
|
||||||
Chan: reflect.ValueOf(channel),
|
Chan: reflect.ValueOf(channel),
|
||||||
@ -221,7 +221,7 @@ func (srvc *Service) handleEvents(peerPipe <-chan newPeerConnection) {
|
|||||||
addPoll(evnt.channel)
|
addPoll(evnt.channel)
|
||||||
srvc.connect(evnt.channel, evnt.peer)
|
srvc.connect(evnt.channel, evnt.peer)
|
||||||
default: // peer event
|
default: // peer event
|
||||||
channel := poll[chosen].Chan.Interface().(chan *protocol.PacketEvent)
|
channel := poll[chosen].Chan.Interface().(chan *PacketEvent)
|
||||||
peer := srvc.getPeer(channel)
|
peer := srvc.getPeer(channel)
|
||||||
if peer == nil {
|
if peer == nil {
|
||||||
log.Printf("Unknown peer event: %v", value)
|
log.Printf("Unknown peer event: %v", value)
|
||||||
@ -229,7 +229,7 @@ func (srvc *Service) handleEvents(peerPipe <-chan newPeerConnection) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
evnt, ok := value.Interface().(*protocol.PacketEvent)
|
evnt, ok := value.Interface().(*PacketEvent)
|
||||||
if !recvOK || !ok || evnt == nil {
|
if !recvOK || !ok || evnt == nil {
|
||||||
// peer disconnected, remove it from our poll queue
|
// peer disconnected, remove it from our poll queue
|
||||||
removePoll(chosen)
|
removePoll(chosen)
|
||||||
@ -250,7 +250,7 @@ func (srvc *Service) handleEvents(peerPipe <-chan newPeerConnection) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srvc *Service) handlePacket(peer *protocol.CNPeer, typeID uint32, pkt protocol.Packet) error {
|
func (srvc *Service) handlePacket(peer *Peer, typeID uint32, pkt protocol.Packet) error {
|
||||||
if hndlr, ok := srvc.packetHandlers[typeID]; ok {
|
if hndlr, ok := srvc.packetHandlers[typeID]; ok {
|
||||||
// fmt.Printf("Handling packet %x\n", typeID)
|
// fmt.Printf("Handling packet %x\n", typeID)
|
||||||
if err := hndlr(peer, pkt); err != nil {
|
if err := hndlr(peer, pkt); err != nil {
|
||||||
@ -263,7 +263,7 @@ func (srvc *Service) handlePacket(peer *protocol.CNPeer, typeID uint32, pkt prot
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srvc *Service) disconnect(channel chan *protocol.PacketEvent, peer *protocol.CNPeer) {
|
func (srvc *Service) disconnect(channel chan *PacketEvent, peer *Peer) {
|
||||||
log.Printf("Peer %p disconnected from %s\n", peer, srvc.Name)
|
log.Printf("Peer %p disconnected from %s\n", peer, srvc.Name)
|
||||||
if srvc.OnDisconnect != nil {
|
if srvc.OnDisconnect != nil {
|
||||||
srvc.OnDisconnect(peer)
|
srvc.OnDisconnect(peer)
|
||||||
@ -272,7 +272,7 @@ func (srvc *Service) disconnect(channel chan *protocol.PacketEvent, peer *protoc
|
|||||||
srvc.removePeer(channel)
|
srvc.removePeer(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srvc *Service) connect(channel chan *protocol.PacketEvent, peer *protocol.CNPeer) {
|
func (srvc *Service) connect(channel chan *PacketEvent, peer *Peer) {
|
||||||
log.Printf("New peer %p connected to %s\n", peer, srvc.Name)
|
log.Printf("New peer %p connected to %s\n", peer, srvc.Name)
|
||||||
if srvc.OnConnect != nil {
|
if srvc.OnConnect != nil {
|
||||||
srvc.OnConnect(peer)
|
srvc.OnConnect(peer)
|
97
cnet/service_test.go
Normal file
97
cnet/service_test.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package cnet_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/CPunch/gopenfusion/cnet"
|
||||||
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
|
"github.com/CPunch/gopenfusion/util"
|
||||||
|
"github.com/matryer/is"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
srvcPort int
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
timeout = 2 * time.Second
|
||||||
|
maxDummyPeers = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
var err error
|
||||||
|
srvcPort, err = cnet.RandomPort()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestService(t *testing.T) {
|
||||||
|
is := is.New(t)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
srvc := cnet.NewService(ctx, "TEST", srvcPort)
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
|
// shutdown service when test is done
|
||||||
|
defer func() {
|
||||||
|
cancel()
|
||||||
|
is.True(util.SelectWithTimeout(srvc.Stopped(), timeout)) // wait for service to stop with timeout
|
||||||
|
}()
|
||||||
|
|
||||||
|
// our dummy packet handler
|
||||||
|
wg.Add(maxDummyPeers)
|
||||||
|
srvc.AddPacketHandler(0x1234, func(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
|
log.Printf("Received packet %#v", pkt)
|
||||||
|
wg.Done()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// wait for all dummy peers to connect and disconnect
|
||||||
|
wg.Add(maxDummyPeers)
|
||||||
|
srvc.OnConnect = func(peer *cnet.Peer) {
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(maxDummyPeers)
|
||||||
|
srvc.OnDisconnect = func(peer *cnet.Peer) {
|
||||||
|
wg.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
// run service
|
||||||
|
go func() { is.NoErr(srvc.Start()) }() // srvc.Start error
|
||||||
|
is.True(util.SelectWithTimeout(srvc.Started(), timeout)) // wait for service to start with timeout
|
||||||
|
|
||||||
|
wg.Add(maxDummyPeers * 2) // 2 wg.Done() per peer for receiving packets
|
||||||
|
for i := 0; i < maxDummyPeers; i++ {
|
||||||
|
go func() {
|
||||||
|
// make dummy client
|
||||||
|
conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", srvcPort))
|
||||||
|
is.NoErr(err) // net.Dial error
|
||||||
|
|
||||||
|
peer := cnet.NewPeer(ctx, conn)
|
||||||
|
go func() {
|
||||||
|
defer peer.Kill()
|
||||||
|
|
||||||
|
// send dummy packets
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
is.NoErr(peer.Send(0x1234)) // peer.Send error
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// we wait until Handler gracefully exits (peer was killed)
|
||||||
|
peer.Handler(make(chan *cnet.PacketEvent))
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
is.True(util.WaitWithTimeout(&wg, timeout)) // wait for all dummy peers to be done with timeout
|
||||||
|
}
|
@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
"github.com/georgysavva/scany/v2/sqlscan"
|
"github.com/georgysavva/scany/v2/sqlscan"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ import (
|
|||||||
|
|
||||||
"github.com/matryer/is"
|
"github.com/matryer/is"
|
||||||
|
|
||||||
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
"github.com/CPunch/gopenfusion/internal/db"
|
"github.com/CPunch/gopenfusion/internal/db"
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
|
||||||
"github.com/bitcomplete/sqltestutil"
|
"github.com/bitcomplete/sqltestutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ package db
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Inventory struct {
|
type Inventory struct {
|
||||||
|
@ -3,8 +3,8 @@ package db
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
"github.com/CPunch/gopenfusion/config"
|
"github.com/CPunch/gopenfusion/config"
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
|
||||||
"github.com/blockloop/scan"
|
"github.com/blockloop/scan"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
package entity
|
|
||||||
|
|
||||||
import "github.com/CPunch/gopenfusion/config"
|
|
||||||
|
|
||||||
type ChunkPosition struct {
|
|
||||||
X int
|
|
||||||
Y int
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeChunkPosition(x, y int) ChunkPosition {
|
|
||||||
return ChunkPosition{
|
|
||||||
X: x / (config.VIEW_DISTANCE / 3),
|
|
||||||
Y: y / (config.VIEW_DISTANCE / 3),
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,108 +0,0 @@
|
|||||||
package service_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
|
||||||
"github.com/CPunch/gopenfusion/internal/service"
|
|
||||||
"github.com/matryer/is"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
srvcPort int
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
timeout = 2
|
|
||||||
maxDummyPeers = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
func selectWithTimeout(ch <-chan struct{}, seconds int) bool {
|
|
||||||
select {
|
|
||||||
case <-ch:
|
|
||||||
return true
|
|
||||||
case <-time.After(time.Duration(seconds) * time.Second):
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitWithTimeout(wg *sync.WaitGroup, seconds int) bool {
|
|
||||||
done := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
defer close(done)
|
|
||||||
wg.Wait()
|
|
||||||
}()
|
|
||||||
|
|
||||||
return selectWithTimeout(done, seconds)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
var err error
|
|
||||||
srvcPort, err = service.RandomPort()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(m.Run())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestService(t *testing.T) {
|
|
||||||
is := is.New(t)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
srvc := service.NewService(ctx, "TEST", srvcPort)
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
|
|
||||||
// shutdown service when test is done
|
|
||||||
defer func() {
|
|
||||||
cancel()
|
|
||||||
is.True(selectWithTimeout(srvc.Stopped(), timeout)) // wait for service to stop with timeout
|
|
||||||
}()
|
|
||||||
|
|
||||||
// our dummy packet handler
|
|
||||||
srvc.AddPacketHandler(0x1234, func(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
|
||||||
log.Printf("Received packet %#v", pkt)
|
|
||||||
wg.Done()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// run service
|
|
||||||
go func() {
|
|
||||||
err := srvc.Start()
|
|
||||||
is.NoErr(err) // srvc.Start error
|
|
||||||
}()
|
|
||||||
|
|
||||||
is.True(selectWithTimeout(srvc.Started(), timeout)) // wait for service to start with timeout
|
|
||||||
|
|
||||||
wg.Add(maxDummyPeers * 3) // 2 wg.Done() calls per dummy peer
|
|
||||||
for i := 0; i < maxDummyPeers; i++ {
|
|
||||||
go func() {
|
|
||||||
// make dummy client
|
|
||||||
conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", srvcPort))
|
|
||||||
is.NoErr(err) // net.Dial error
|
|
||||||
|
|
||||||
peer := protocol.NewCNPeer(ctx, conn)
|
|
||||||
go func() {
|
|
||||||
defer peer.Kill()
|
|
||||||
|
|
||||||
// send dummy packets
|
|
||||||
for i := 0; i < 2; i++ {
|
|
||||||
err := peer.Send(0x1234)
|
|
||||||
is.NoErr(err) // peer.Send error
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// we wait until Handler gracefully exits (peer was killed)
|
|
||||||
peer.Handler(make(chan *protocol.PacketEvent))
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
is.True(waitWithTimeout(&wg, timeout)) // wait for all dummy peers to be done with timeout
|
|
||||||
}
|
|
@ -7,10 +7,12 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/CPunch/gopenfusion/cnet"
|
||||||
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
"github.com/CPunch/gopenfusion/config"
|
"github.com/CPunch/gopenfusion/config"
|
||||||
"github.com/CPunch/gopenfusion/internal/db"
|
"github.com/CPunch/gopenfusion/internal/db"
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
|
||||||
"github.com/CPunch/gopenfusion/internal/redis"
|
"github.com/CPunch/gopenfusion/internal/redis"
|
||||||
|
"github.com/CPunch/gopenfusion/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -25,14 +27,14 @@ const (
|
|||||||
LOGIN_UPDATED_EUALA_REQUIRED = 9
|
LOGIN_UPDATED_EUALA_REQUIRED = 9
|
||||||
)
|
)
|
||||||
|
|
||||||
func (server *LoginServer) AcceptLogin(peer *protocol.CNPeer, SzID string, IClientVerC int32, ISlotNum int8, data []protocol.SP_LS2CL_REP_CHAR_INFO) error {
|
func (server *LoginServer) AcceptLogin(peer *cnet.Peer, SzID string, IClientVerC int32, ISlotNum int8, data []protocol.SP_LS2CL_REP_CHAR_INFO) error {
|
||||||
resp := protocol.SP_LS2CL_REP_LOGIN_SUCC{
|
resp := protocol.SP_LS2CL_REP_LOGIN_SUCC{
|
||||||
SzID: SzID,
|
SzID: SzID,
|
||||||
ICharCount: int8(len(data)),
|
ICharCount: int8(len(data)),
|
||||||
ISlotNum: ISlotNum,
|
ISlotNum: ISlotNum,
|
||||||
IPaymentFlag: 1,
|
IPaymentFlag: 1,
|
||||||
IOpenBetaFlag: 0,
|
IOpenBetaFlag: 0,
|
||||||
UiSvrTime: protocol.GetTime(),
|
UiSvrTime: util.GetTime(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := peer.Send(protocol.P_LS2CL_REP_LOGIN_SUCC, resp); err != nil {
|
if err := peer.Send(protocol.P_LS2CL_REP_LOGIN_SUCC, resp); err != nil {
|
||||||
@ -61,7 +63,7 @@ func (server *LoginServer) AcceptLogin(peer *protocol.CNPeer, SzID string, IClie
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *LoginServer) Login(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *LoginServer) Login(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var loginPkt protocol.SP_CL2LS_REQ_LOGIN
|
var loginPkt protocol.SP_CL2LS_REQ_LOGIN
|
||||||
pkt.Decode(&loginPkt)
|
pkt.Decode(&loginPkt)
|
||||||
|
|
||||||
@ -137,7 +139,7 @@ func (server *LoginServer) Login(peer *protocol.CNPeer, pkt protocol.Packet) err
|
|||||||
return server.AcceptLogin(peer, loginPkt.SzID, loginPkt.IClientVerC, 1, charInfo[:len(plrs)])
|
return server.AcceptLogin(peer, loginPkt.SzID, loginPkt.IClientVerC, 1, charInfo[:len(plrs)])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *LoginServer) CheckCharacterName(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *LoginServer) CheckCharacterName(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var charPkt protocol.SP_CL2LS_REQ_CHECK_CHAR_NAME
|
var charPkt protocol.SP_CL2LS_REQ_CHECK_CHAR_NAME
|
||||||
pkt.Decode(&charPkt)
|
pkt.Decode(&charPkt)
|
||||||
|
|
||||||
@ -148,7 +150,7 @@ func (server *LoginServer) CheckCharacterName(peer *protocol.CNPeer, pkt protoco
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *LoginServer) SaveCharacterName(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *LoginServer) SaveCharacterName(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var charPkt protocol.SP_CL2LS_REQ_SAVE_CHAR_NAME
|
var charPkt protocol.SP_CL2LS_REQ_SAVE_CHAR_NAME
|
||||||
pkt.Decode(&charPkt)
|
pkt.Decode(&charPkt)
|
||||||
|
|
||||||
@ -201,7 +203,7 @@ func validateCharacterCreation(character *protocol.SP_CL2LS_REQ_CHAR_CREATE) boo
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendFail(peer *protocol.CNPeer) error {
|
func SendFail(peer *cnet.Peer) error {
|
||||||
if err := peer.Send(protocol.P_LS2CL_REP_SHARD_SELECT_FAIL, protocol.SP_LS2CL_REP_SHARD_SELECT_FAIL{
|
if err := peer.Send(protocol.P_LS2CL_REP_SHARD_SELECT_FAIL, protocol.SP_LS2CL_REP_SHARD_SELECT_FAIL{
|
||||||
IErrorCode: 2,
|
IErrorCode: 2,
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
@ -211,7 +213,7 @@ func SendFail(peer *protocol.CNPeer) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *LoginServer) CharacterCreate(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *LoginServer) CharacterCreate(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var charPkt protocol.SP_CL2LS_REQ_CHAR_CREATE
|
var charPkt protocol.SP_CL2LS_REQ_CHAR_CREATE
|
||||||
pkt.Decode(&charPkt)
|
pkt.Decode(&charPkt)
|
||||||
|
|
||||||
@ -244,7 +246,7 @@ func (server *LoginServer) CharacterCreate(peer *protocol.CNPeer, pkt protocol.P
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *LoginServer) CharacterDelete(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *LoginServer) CharacterDelete(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var charPkt protocol.SP_CL2LS_REQ_CHAR_DELETE
|
var charPkt protocol.SP_CL2LS_REQ_CHAR_DELETE
|
||||||
pkt.Decode(&charPkt)
|
pkt.Decode(&charPkt)
|
||||||
|
|
||||||
@ -263,7 +265,7 @@ func (server *LoginServer) CharacterDelete(peer *protocol.CNPeer, pkt protocol.P
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *LoginServer) ShardSelect(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *LoginServer) ShardSelect(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var selection protocol.SP_CL2LS_REQ_CHAR_SELECT
|
var selection protocol.SP_CL2LS_REQ_CHAR_SELECT
|
||||||
pkt.Decode(&selection)
|
pkt.Decode(&selection)
|
||||||
|
|
||||||
@ -319,7 +321,7 @@ func (server *LoginServer) ShardSelect(peer *protocol.CNPeer, pkt protocol.Packe
|
|||||||
return peer.Send(protocol.P_LS2CL_REP_SHARD_SELECT_SUCC, resp)
|
return peer.Send(protocol.P_LS2CL_REP_SHARD_SELECT_SUCC, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *LoginServer) FinishTutorial(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *LoginServer) FinishTutorial(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var charPkt protocol.SP_CL2LS_REQ_SAVE_CHAR_TUTOR
|
var charPkt protocol.SP_CL2LS_REQ_SAVE_CHAR_TUTOR
|
||||||
pkt.Decode(&charPkt)
|
pkt.Decode(&charPkt)
|
||||||
|
|
||||||
|
@ -3,20 +3,20 @@ package login
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/CPunch/gopenfusion/cnet"
|
||||||
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
"github.com/CPunch/gopenfusion/internal/db"
|
"github.com/CPunch/gopenfusion/internal/db"
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
|
||||||
"github.com/CPunch/gopenfusion/internal/redis"
|
"github.com/CPunch/gopenfusion/internal/redis"
|
||||||
"github.com/CPunch/gopenfusion/internal/service"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginServer struct {
|
type LoginServer struct {
|
||||||
service *service.Service
|
service *cnet.Service
|
||||||
dbHndlr *db.DBHandler
|
dbHndlr *db.DBHandler
|
||||||
redisHndlr *redis.RedisHandler
|
redisHndlr *redis.RedisHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLoginServer(ctx context.Context, dbHndlr *db.DBHandler, redisHndlr *redis.RedisHandler, port int) (*LoginServer, error) {
|
func NewLoginServer(ctx context.Context, dbHndlr *db.DBHandler, redisHndlr *redis.RedisHandler, port int) (*LoginServer, error) {
|
||||||
srvc := service.NewService(ctx, "LOGIN", port)
|
srvc := cnet.NewService(ctx, "LOGIN", port)
|
||||||
|
|
||||||
server := &LoginServer{
|
server := &LoginServer{
|
||||||
service: srvc,
|
service: srvc,
|
||||||
@ -30,14 +30,14 @@ func NewLoginServer(ctx context.Context, dbHndlr *db.DBHandler, redisHndlr *redi
|
|||||||
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_CHAR_CREATE, server.CharacterCreate)
|
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_CHAR_CREATE, server.CharacterCreate)
|
||||||
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_CHAR_SELECT, server.ShardSelect)
|
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_CHAR_SELECT, server.ShardSelect)
|
||||||
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_CHAR_DELETE, server.CharacterDelete)
|
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_CHAR_DELETE, server.CharacterDelete)
|
||||||
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_SHARD_SELECT, service.StubbedPacket)
|
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_SHARD_SELECT, cnet.StubbedPacket)
|
||||||
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_SHARD_LIST_INFO, service.StubbedPacket)
|
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_SHARD_LIST_INFO, cnet.StubbedPacket)
|
||||||
srvc.AddPacketHandler(protocol.P_CL2LS_CHECK_NAME_LIST, service.StubbedPacket)
|
srvc.AddPacketHandler(protocol.P_CL2LS_CHECK_NAME_LIST, cnet.StubbedPacket)
|
||||||
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_SAVE_CHAR_TUTOR, server.FinishTutorial)
|
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_SAVE_CHAR_TUTOR, server.FinishTutorial)
|
||||||
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_PC_EXIT_DUPLICATE, service.StubbedPacket)
|
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_PC_EXIT_DUPLICATE, cnet.StubbedPacket)
|
||||||
srvc.AddPacketHandler(protocol.P_CL2LS_REP_LIVE_CHECK, service.StubbedPacket)
|
srvc.AddPacketHandler(protocol.P_CL2LS_REP_LIVE_CHECK, cnet.StubbedPacket)
|
||||||
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_CHANGE_CHAR_NAME, service.StubbedPacket)
|
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_CHANGE_CHAR_NAME, cnet.StubbedPacket)
|
||||||
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_SERVER_SELECT, service.StubbedPacket)
|
srvc.AddPacketHandler(protocol.P_CL2LS_REQ_SERVER_SELECT, cnet.StubbedPacket)
|
||||||
|
|
||||||
return server, nil
|
return server, nil
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,12 @@ package shard
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/internal/entity"
|
"github.com/CPunch/gopenfusion/cnet"
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
|
"github.com/CPunch/gopenfusion/shard/entity"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (server *ShardServer) freeChat(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *ShardServer) freeChat(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var chat protocol.SP_CL2FE_REQ_SEND_FREECHAT_MESSAGE
|
var chat protocol.SP_CL2FE_REQ_SEND_FREECHAT_MESSAGE
|
||||||
pkt.Decode(&chat)
|
pkt.Decode(&chat)
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ func (server *ShardServer) freeChat(peer *protocol.CNPeer, pkt protocol.Packet)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *ShardServer) menuChat(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *ShardServer) menuChat(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var chat protocol.SP_CL2FE_REQ_SEND_MENUCHAT_MESSAGE
|
var chat protocol.SP_CL2FE_REQ_SEND_MENUCHAT_MESSAGE
|
||||||
pkt.Decode(&chat)
|
pkt.Decode(&chat)
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ func (server *ShardServer) menuChat(peer *protocol.CNPeer, pkt protocol.Packet)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *ShardServer) emoteChat(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *ShardServer) emoteChat(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var chat protocol.SP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT
|
var chat protocol.SP_CL2FE_REQ_PC_AVATAR_EMOTES_CHAT
|
||||||
pkt.Decode(&chat)
|
pkt.Decode(&chat)
|
||||||
|
|
||||||
|
@ -3,8 +3,22 @@ package entity
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/CPunch/gopenfusion/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type ChunkPosition struct {
|
||||||
|
X int
|
||||||
|
Y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeChunkPosition(x, y int) ChunkPosition {
|
||||||
|
return ChunkPosition{
|
||||||
|
X: x / (config.VIEW_DISTANCE / 3),
|
||||||
|
Y: y / (config.VIEW_DISTANCE / 3),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type Chunk struct {
|
type Chunk struct {
|
||||||
Position ChunkPosition
|
Position ChunkPosition
|
||||||
entities map[Entity]struct{}
|
entities map[Entity]struct{}
|
@ -1,6 +1,6 @@
|
|||||||
package entity
|
package entity
|
||||||
|
|
||||||
import "github.com/CPunch/gopenfusion/internal/protocol"
|
import "github.com/CPunch/gopenfusion/cnet"
|
||||||
|
|
||||||
type EntityKind int
|
type EntityKind int
|
||||||
|
|
||||||
@ -20,6 +20,6 @@ type Entity interface {
|
|||||||
SetPosition(x, y, z int)
|
SetPosition(x, y, z int)
|
||||||
SetAngle(angle int)
|
SetAngle(angle int)
|
||||||
|
|
||||||
DisappearFromViewOf(peer *protocol.CNPeer)
|
DisappearFromViewOf(peer *cnet.Peer)
|
||||||
EnterIntoViewOf(peer *protocol.CNPeer)
|
EnterIntoViewOf(peer *cnet.Peer)
|
||||||
}
|
}
|
@ -3,7 +3,7 @@ package entity_test
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/internal/entity"
|
"github.com/CPunch/gopenfusion/shard/entity"
|
||||||
"github.com/matryer/is"
|
"github.com/matryer/is"
|
||||||
)
|
)
|
||||||
|
|
@ -3,7 +3,8 @@ package entity
|
|||||||
import (
|
import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
"github.com/CPunch/gopenfusion/cnet"
|
||||||
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NPC struct {
|
type NPC struct {
|
||||||
@ -62,13 +63,13 @@ func (npc *NPC) SetAngle(angle int) {
|
|||||||
npc.Angle = angle
|
npc.Angle = angle
|
||||||
}
|
}
|
||||||
|
|
||||||
func (npc *NPC) DisappearFromViewOf(peer *protocol.CNPeer) {
|
func (npc *NPC) DisappearFromViewOf(peer *cnet.Peer) {
|
||||||
peer.Send(protocol.P_FE2CL_NPC_EXIT, protocol.SP_FE2CL_NPC_EXIT{
|
peer.Send(protocol.P_FE2CL_NPC_EXIT, protocol.SP_FE2CL_NPC_EXIT{
|
||||||
INPC_ID: int32(npc.ID),
|
INPC_ID: int32(npc.ID),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (npc *NPC) EnterIntoViewOf(peer *protocol.CNPeer) {
|
func (npc *NPC) EnterIntoViewOf(peer *cnet.Peer) {
|
||||||
peer.Send(protocol.P_FE2CL_NPC_NEW, protocol.SP_FE2CL_NPC_NEW{
|
peer.Send(protocol.P_FE2CL_NPC_NEW, protocol.SP_FE2CL_NPC_NEW{
|
||||||
NPCAppearanceData: npc.GetAppearanceData(),
|
NPCAppearanceData: npc.GetAppearanceData(),
|
||||||
})
|
})
|
@ -1,17 +1,18 @@
|
|||||||
package entity
|
package entity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/CPunch/gopenfusion/cnet"
|
||||||
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
"github.com/CPunch/gopenfusion/internal/db"
|
"github.com/CPunch/gopenfusion/internal/db"
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Player struct {
|
type Player struct {
|
||||||
db.Player
|
db.Player
|
||||||
Peer *protocol.CNPeer
|
Peer *cnet.Peer
|
||||||
Chunk ChunkPosition
|
Chunk ChunkPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPlayer(peer *protocol.CNPeer, player *db.Player) *Player {
|
func NewPlayer(peer *cnet.Peer, player *db.Player) *Player {
|
||||||
return &Player{
|
return &Player{
|
||||||
Player: *player,
|
Player: *player,
|
||||||
Peer: peer,
|
Peer: peer,
|
||||||
@ -51,13 +52,13 @@ func (plr *Player) SetAngle(angle int) {
|
|||||||
plr.Angle = angle
|
plr.Angle = angle
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plr *Player) DisappearFromViewOf(peer *protocol.CNPeer) {
|
func (plr *Player) DisappearFromViewOf(peer *cnet.Peer) {
|
||||||
peer.Send(protocol.P_FE2CL_PC_EXIT, protocol.SP_FE2CL_PC_EXIT{
|
peer.Send(protocol.P_FE2CL_PC_EXIT, protocol.SP_FE2CL_PC_EXIT{
|
||||||
IID: int32(plr.PlayerID),
|
IID: int32(plr.PlayerID),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plr *Player) EnterIntoViewOf(peer *protocol.CNPeer) {
|
func (plr *Player) EnterIntoViewOf(peer *cnet.Peer) {
|
||||||
peer.Send(protocol.P_FE2CL_PC_NEW, protocol.SP_FE2CL_PC_NEW{
|
peer.Send(protocol.P_FE2CL_PC_NEW, protocol.SP_FE2CL_PC_NEW{
|
||||||
PCAppearanceData: plr.GetAppearanceData(),
|
PCAppearanceData: plr.GetAppearanceData(),
|
||||||
})
|
})
|
@ -1,7 +1,7 @@
|
|||||||
package shard
|
package shard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/CPunch/gopenfusion/internal/entity"
|
"github.com/CPunch/gopenfusion/shard/entity"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (server *ShardServer) addEntity(e entity.Entity) {
|
func (server *ShardServer) addEntity(e entity.Entity) {
|
||||||
|
@ -4,12 +4,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/internal/entity"
|
"github.com/CPunch/gopenfusion/cnet"
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
"github.com/CPunch/gopenfusion/internal/redis"
|
"github.com/CPunch/gopenfusion/internal/redis"
|
||||||
|
"github.com/CPunch/gopenfusion/shard/entity"
|
||||||
|
"github.com/CPunch/gopenfusion/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (server *ShardServer) attachPlayer(peer *protocol.CNPeer, meta redis.LoginMetadata) (*entity.Player, error) {
|
func (server *ShardServer) attachPlayer(peer *cnet.Peer, meta redis.LoginMetadata) (*entity.Player, error) {
|
||||||
dbPlr, err := server.dbHndlr.GetPlayer(int(meta.PlayerID))
|
dbPlr, err := server.dbHndlr.GetPlayer(int(meta.PlayerID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -24,7 +26,7 @@ func (server *ShardServer) attachPlayer(peer *protocol.CNPeer, meta redis.LoginM
|
|||||||
return plr, nil
|
return plr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *ShardServer) RequestEnter(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *ShardServer) RequestEnter(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var enter protocol.SP_CL2FE_REQ_PC_ENTER
|
var enter protocol.SP_CL2FE_REQ_PC_ENTER
|
||||||
pkt.Decode(&enter)
|
pkt.Decode(&enter)
|
||||||
|
|
||||||
@ -49,13 +51,13 @@ func (server *ShardServer) RequestEnter(peer *protocol.CNPeer, pkt protocol.Pack
|
|||||||
resp := &protocol.SP_FE2CL_REP_PC_ENTER_SUCC{
|
resp := &protocol.SP_FE2CL_REP_PC_ENTER_SUCC{
|
||||||
IID: int32(plr.PlayerID),
|
IID: int32(plr.PlayerID),
|
||||||
PCLoadData2CL: plr.ToPCLoadData2CL(),
|
PCLoadData2CL: plr.ToPCLoadData2CL(),
|
||||||
UiSvrTime: protocol.GetTime(),
|
UiSvrTime: util.GetTime(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup peer
|
// setup peer
|
||||||
peer.E_key = protocol.CreateNewKey(resp.UiSvrTime, uint64(resp.IID+1), uint64(resp.PCLoadData2CL.IFusionMatter+1))
|
peer.E_key = protocol.CreateNewKey(resp.UiSvrTime, uint64(resp.IID+1), uint64(resp.PCLoadData2CL.IFusionMatter+1))
|
||||||
peer.FE_key = loginData.FEKey
|
peer.FE_key = loginData.FEKey
|
||||||
peer.SetActiveKey(protocol.USE_FE)
|
peer.SetActiveKey(cnet.USE_FE)
|
||||||
|
|
||||||
log.Printf("Player %d (AccountID %d) entered\n", resp.IID, loginData.AccountID)
|
log.Printf("Player %d (AccountID %d) entered\n", resp.IID, loginData.AccountID)
|
||||||
if err := peer.Send(protocol.P_FE2CL_REP_PC_ENTER_SUCC, resp); err != nil {
|
if err := peer.Send(protocol.P_FE2CL_REP_PC_ENTER_SUCC, resp); err != nil {
|
||||||
@ -65,7 +67,7 @@ func (server *ShardServer) RequestEnter(peer *protocol.CNPeer, pkt protocol.Pack
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *ShardServer) LoadingComplete(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *ShardServer) LoadingComplete(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var loadComplete protocol.SP_CL2FE_REQ_PC_LOADING_COMPLETE
|
var loadComplete protocol.SP_CL2FE_REQ_PC_LOADING_COMPLETE
|
||||||
pkt.Decode(&loadComplete)
|
pkt.Decode(&loadComplete)
|
||||||
|
|
||||||
|
@ -3,8 +3,10 @@ package shard
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/internal/entity"
|
"github.com/CPunch/gopenfusion/cnet"
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
|
"github.com/CPunch/gopenfusion/shard/entity"
|
||||||
|
"github.com/CPunch/gopenfusion/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (server *ShardServer) updatePlayerPosition(plr *entity.Player, X, Y, Z, Angle int) {
|
func (server *ShardServer) updatePlayerPosition(plr *entity.Player, X, Y, Z, Angle int) {
|
||||||
@ -15,7 +17,7 @@ func (server *ShardServer) updatePlayerPosition(plr *entity.Player, X, Y, Z, Ang
|
|||||||
server.updateEntityChunk(plr, plr.GetChunkPos(), entity.MakeChunkPosition(X, Y))
|
server.updateEntityChunk(plr, plr.GetChunkPos(), entity.MakeChunkPosition(X, Y))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *ShardServer) playerMove(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *ShardServer) playerMove(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var move protocol.SP_CL2FE_REQ_PC_MOVE
|
var move protocol.SP_CL2FE_REQ_PC_MOVE
|
||||||
pkt.Decode(&move)
|
pkt.Decode(&move)
|
||||||
|
|
||||||
@ -39,11 +41,11 @@ func (server *ShardServer) playerMove(peer *protocol.CNPeer, pkt protocol.Packet
|
|||||||
CKeyValue: move.CKeyValue,
|
CKeyValue: move.CKeyValue,
|
||||||
ISpeed: move.ISpeed,
|
ISpeed: move.ISpeed,
|
||||||
IID: int32(plr.PlayerID),
|
IID: int32(plr.PlayerID),
|
||||||
ISvrTime: protocol.GetTime(),
|
ISvrTime: util.GetTime(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *ShardServer) playerStop(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *ShardServer) playerStop(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var stop protocol.SP_CL2FE_REQ_PC_STOP
|
var stop protocol.SP_CL2FE_REQ_PC_STOP
|
||||||
pkt.Decode(&stop)
|
pkt.Decode(&stop)
|
||||||
|
|
||||||
@ -61,11 +63,11 @@ func (server *ShardServer) playerStop(peer *protocol.CNPeer, pkt protocol.Packet
|
|||||||
IY: stop.IY,
|
IY: stop.IY,
|
||||||
IZ: stop.IZ,
|
IZ: stop.IZ,
|
||||||
IID: int32(plr.PlayerID),
|
IID: int32(plr.PlayerID),
|
||||||
ISvrTime: protocol.GetTime(),
|
ISvrTime: util.GetTime(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *ShardServer) playerJump(peer *protocol.CNPeer, pkt protocol.Packet) error {
|
func (server *ShardServer) playerJump(peer *cnet.Peer, pkt protocol.Packet) error {
|
||||||
var jump protocol.SP_CL2FE_REQ_PC_JUMP
|
var jump protocol.SP_CL2FE_REQ_PC_JUMP
|
||||||
pkt.Decode(&jump)
|
pkt.Decode(&jump)
|
||||||
|
|
||||||
@ -89,6 +91,6 @@ func (server *ShardServer) playerJump(peer *protocol.CNPeer, pkt protocol.Packet
|
|||||||
CKeyValue: jump.CKeyValue,
|
CKeyValue: jump.CKeyValue,
|
||||||
ISpeed: jump.ISpeed,
|
ISpeed: jump.ISpeed,
|
||||||
IID: int32(plr.PlayerID),
|
IID: int32(plr.PlayerID),
|
||||||
ISvrTime: protocol.GetTime(),
|
ISvrTime: util.GetTime(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/CPunch/gopenfusion/config"
|
"github.com/CPunch/gopenfusion/config"
|
||||||
"github.com/CPunch/gopenfusion/internal/entity"
|
"github.com/CPunch/gopenfusion/shard/entity"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NPCData struct {
|
type NPCData struct {
|
||||||
|
@ -3,25 +3,25 @@ package shard
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/CPunch/gopenfusion/cnet"
|
||||||
|
"github.com/CPunch/gopenfusion/cnet/protocol"
|
||||||
"github.com/CPunch/gopenfusion/config"
|
"github.com/CPunch/gopenfusion/config"
|
||||||
"github.com/CPunch/gopenfusion/internal/db"
|
"github.com/CPunch/gopenfusion/internal/db"
|
||||||
"github.com/CPunch/gopenfusion/internal/entity"
|
|
||||||
"github.com/CPunch/gopenfusion/internal/protocol"
|
|
||||||
"github.com/CPunch/gopenfusion/internal/redis"
|
"github.com/CPunch/gopenfusion/internal/redis"
|
||||||
"github.com/CPunch/gopenfusion/internal/service"
|
"github.com/CPunch/gopenfusion/shard/entity"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PacketHandler func(peer *protocol.CNPeer, pkt protocol.Packet) error
|
type PacketHandler func(peer *cnet.Peer, pkt protocol.Packet) error
|
||||||
|
|
||||||
type ShardServer struct {
|
type ShardServer struct {
|
||||||
service *service.Service
|
service *cnet.Service
|
||||||
dbHndlr *db.DBHandler
|
dbHndlr *db.DBHandler
|
||||||
redisHndlr *redis.RedisHandler
|
redisHndlr *redis.RedisHandler
|
||||||
chunks map[entity.ChunkPosition]*entity.Chunk
|
chunks map[entity.ChunkPosition]*entity.Chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewShardServer(ctx context.Context, dbHndlr *db.DBHandler, redisHndlr *redis.RedisHandler, port int) (*ShardServer, error) {
|
func NewShardServer(ctx context.Context, dbHndlr *db.DBHandler, redisHndlr *redis.RedisHandler, port int) (*ShardServer, error) {
|
||||||
srvc := service.NewService(ctx, "SHARD", port)
|
srvc := cnet.NewService(ctx, "SHARD", port)
|
||||||
|
|
||||||
server := &ShardServer{
|
server := &ShardServer{
|
||||||
service: srvc,
|
service: srvc,
|
||||||
@ -55,7 +55,7 @@ func (server *ShardServer) Start() {
|
|||||||
server.service.Start()
|
server.service.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *ShardServer) onDisconnect(peer *protocol.CNPeer) {
|
func (server *ShardServer) onDisconnect(peer *cnet.Peer) {
|
||||||
// remove from chunks
|
// remove from chunks
|
||||||
plr, ok := peer.UserData().(*entity.Player)
|
plr, ok := peer.UserData().(*entity.Player)
|
||||||
if ok && plr != nil {
|
if ok && plr != nil {
|
||||||
@ -63,6 +63,6 @@ func (server *ShardServer) onDisconnect(peer *protocol.CNPeer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *ShardServer) onConnect(peer *protocol.CNPeer) {
|
func (server *ShardServer) onConnect(peer *cnet.Peer) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
29
util/util.go
Normal file
29
util/util.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetTime() uint64 {
|
||||||
|
return uint64(time.Now().UnixMilli())
|
||||||
|
}
|
||||||
|
|
||||||
|
func SelectWithTimeout(ch <-chan struct{}, timeout time.Duration) bool {
|
||||||
|
select {
|
||||||
|
case <-ch:
|
||||||
|
return true
|
||||||
|
case <-time.After(timeout):
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WaitWithTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(done)
|
||||||
|
wg.Wait()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return SelectWithTimeout(done, timeout)
|
||||||
|
}
|
35
util/util_test.go
Normal file
35
util/util_test.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package util_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/CPunch/gopenfusion/util"
|
||||||
|
"github.com/matryer/is"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWaitWithTimeout(t *testing.T) {
|
||||||
|
is := is.New(t)
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
go func() {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
is.True(!util.WaitWithTimeout(wg, 500*time.Millisecond)) // timeout should occur
|
||||||
|
is.True(util.WaitWithTimeout(wg, 750*time.Millisecond)) // timeout shouldn't occur
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectWithTimeout(t *testing.T) {
|
||||||
|
is := is.New(t)
|
||||||
|
ch := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
close(ch)
|
||||||
|
}()
|
||||||
|
|
||||||
|
is.True(!util.SelectWithTimeout(ch, 500*time.Millisecond)) // timeout should occur
|
||||||
|
is.True(util.SelectWithTimeout(ch, 750*time.Millisecond)) // timeout shouldn't occur
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user