2025-01-13 11:10:48 +01:00
|
|
|
package network
|
2024-10-31 01:06:39 +01:00
|
|
|
|
|
|
|
import (
|
2025-01-15 10:50:51 +01:00
|
|
|
"bufio"
|
2025-02-14 13:35:09 +01:00
|
|
|
"context"
|
2025-01-15 10:50:51 +01:00
|
|
|
"encoding/binary"
|
2025-01-19 21:23:47 +01:00
|
|
|
"fmt"
|
2025-01-15 10:50:51 +01:00
|
|
|
"io"
|
2024-10-31 01:06:39 +01:00
|
|
|
"log"
|
|
|
|
"net"
|
2025-02-14 13:35:09 +01:00
|
|
|
"sync"
|
2024-10-31 01:06:39 +01:00
|
|
|
"time"
|
|
|
|
|
2025-02-14 13:35:09 +01:00
|
|
|
"gitea.boner.be/bdnugget/goonscape/logging"
|
2025-01-13 11:10:48 +01:00
|
|
|
"gitea.boner.be/bdnugget/goonscape/types"
|
2024-10-31 01:06:39 +01:00
|
|
|
pb "gitea.boner.be/bdnugget/goonserver/actions"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
)
|
|
|
|
|
2025-01-19 21:52:56 +01:00
|
|
|
const protoVersion = 1
|
|
|
|
|
2025-01-18 14:23:48 +01:00
|
|
|
var serverAddr = "boner.be:6969"
|
|
|
|
|
2025-02-14 13:35:09 +01:00
|
|
|
type NetworkManager struct {
|
|
|
|
ctx context.Context
|
|
|
|
conn net.Conn
|
|
|
|
reader *bufio.Reader
|
|
|
|
writer *bufio.Writer
|
|
|
|
mu sync.Mutex
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewNetworkManager(ctx context.Context) *NetworkManager {
|
|
|
|
return &NetworkManager{
|
|
|
|
ctx: ctx,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-01-18 14:23:48 +01:00
|
|
|
func SetServerAddr(addr string) {
|
|
|
|
serverAddr = addr
|
|
|
|
}
|
|
|
|
|
2025-02-14 13:35:09 +01:00
|
|
|
func (nm *NetworkManager) Connect(addr string) error {
|
|
|
|
nm.mu.Lock()
|
|
|
|
defer nm.mu.Unlock()
|
|
|
|
|
|
|
|
var err error
|
|
|
|
nm.conn, err = net.Dial("tcp", addr)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
nm.reader = bufio.NewReader(nm.conn)
|
|
|
|
nm.writer = bufio.NewWriter(nm.conn)
|
|
|
|
|
|
|
|
go nm.readLoop()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nm *NetworkManager) readLoop() {
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-nm.ctx.Done():
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
// Read and process messages
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nm *NetworkManager) Send(message proto.Message) error {
|
|
|
|
nm.mu.Lock()
|
|
|
|
defer nm.mu.Unlock()
|
|
|
|
|
|
|
|
data, err := proto.Marshal(message)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write length prefix
|
|
|
|
lengthBuf := make([]byte, 4)
|
|
|
|
binary.BigEndian.PutUint32(lengthBuf, uint32(len(data)))
|
|
|
|
if _, err := nm.writer.Write(lengthBuf); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write message body
|
|
|
|
if _, err := nm.writer.Write(data); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nm.writer.Flush()
|
|
|
|
}
|
|
|
|
|
2025-01-19 21:23:47 +01:00
|
|
|
func ConnectToServer(username, password string, isRegistering bool) (net.Conn, int32, error) {
|
2025-02-14 13:35:09 +01:00
|
|
|
logging.Info.Println("Attempting to connect to server at", serverAddr)
|
2025-01-18 14:23:48 +01:00
|
|
|
conn, err := net.Dial("tcp", serverAddr)
|
2024-10-31 01:06:39 +01:00
|
|
|
if err != nil {
|
2025-02-14 13:35:09 +01:00
|
|
|
logging.Error.Printf("Failed to dial server: %v", err)
|
2024-10-31 01:06:39 +01:00
|
|
|
return nil, 0, err
|
|
|
|
}
|
2025-02-14 13:35:09 +01:00
|
|
|
logging.Info.Println("Connected to server. Authenticating...")
|
2025-01-19 21:23:47 +01:00
|
|
|
|
|
|
|
// Send auth message
|
|
|
|
authAction := &pb.Action{
|
|
|
|
Type: pb.Action_LOGIN,
|
|
|
|
Username: username,
|
|
|
|
Password: password,
|
|
|
|
}
|
|
|
|
if isRegistering {
|
|
|
|
authAction.Type = pb.Action_REGISTER
|
|
|
|
}
|
|
|
|
|
|
|
|
authBatch := &pb.ActionBatch{
|
2025-01-19 21:52:56 +01:00
|
|
|
Actions: []*pb.Action{authAction},
|
|
|
|
ProtocolVersion: protoVersion,
|
2025-01-19 21:23:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := writeMessage(conn, authBatch); err != nil {
|
|
|
|
conn.Close()
|
|
|
|
return nil, 0, fmt.Errorf("failed to send auth: %v", err)
|
|
|
|
}
|
2025-01-15 10:58:11 +01:00
|
|
|
|
2025-01-19 21:23:47 +01:00
|
|
|
// Read server response
|
|
|
|
reader := bufio.NewReader(conn)
|
2025-01-15 10:58:11 +01:00
|
|
|
lengthBuf := make([]byte, 4)
|
|
|
|
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
|
2025-01-19 21:23:47 +01:00
|
|
|
conn.Close()
|
|
|
|
return nil, 0, fmt.Errorf("failed to read auth response: %v", err)
|
2025-01-15 10:58:11 +01:00
|
|
|
}
|
|
|
|
messageLength := binary.BigEndian.Uint32(lengthBuf)
|
|
|
|
|
|
|
|
messageBuf := make([]byte, messageLength)
|
|
|
|
if _, err := io.ReadFull(reader, messageBuf); err != nil {
|
2025-01-19 21:23:47 +01:00
|
|
|
conn.Close()
|
|
|
|
return nil, 0, fmt.Errorf("failed to read auth response body: %v", err)
|
2024-10-31 01:06:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
var response pb.ServerMessage
|
2025-01-15 10:58:11 +01:00
|
|
|
if err := proto.Unmarshal(messageBuf, &response); err != nil {
|
2025-01-19 21:23:47 +01:00
|
|
|
conn.Close()
|
|
|
|
return nil, 0, fmt.Errorf("failed to unmarshal auth response: %v", err)
|
|
|
|
}
|
|
|
|
|
2025-01-19 21:52:56 +01:00
|
|
|
if response.ProtocolVersion > protoVersion {
|
|
|
|
conn.Close()
|
|
|
|
return nil, 0, fmt.Errorf("server requires newer protocol version (server: %d, client: %d)",
|
|
|
|
response.ProtocolVersion, protoVersion)
|
|
|
|
}
|
|
|
|
|
2025-01-19 21:23:47 +01:00
|
|
|
if !response.AuthSuccess {
|
|
|
|
conn.Close()
|
2025-02-14 13:35:09 +01:00
|
|
|
return nil, 0, fmt.Errorf("authentication failed: %s", response.ErrorMessage)
|
2024-10-31 01:06:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
playerID := response.GetPlayerId()
|
2025-01-19 21:23:47 +01:00
|
|
|
log.Printf("Successfully authenticated with player ID: %d", playerID)
|
2024-10-31 01:06:39 +01:00
|
|
|
return conn, playerID, nil
|
|
|
|
}
|
|
|
|
|
2025-02-14 13:35:09 +01:00
|
|
|
func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Player, otherPlayers *sync.Map, quitChan <-chan struct{}) {
|
|
|
|
defer func() {
|
|
|
|
logging.Info.Println("Closing connection and cleaning up for player", playerID)
|
|
|
|
conn.Close()
|
|
|
|
close(player.QuitDone)
|
2024-10-31 01:06:39 +01:00
|
|
|
}()
|
|
|
|
|
2025-02-14 13:35:09 +01:00
|
|
|
reader := bufio.NewReader(conn)
|
|
|
|
ticker := time.NewTicker(100 * time.Millisecond)
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
2025-01-23 09:42:42 +01:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-quitChan:
|
|
|
|
return
|
2025-02-14 13:35:09 +01:00
|
|
|
case <-ticker.C:
|
|
|
|
// Read message length
|
2025-01-23 09:42:42 +01:00
|
|
|
lengthBuf := make([]byte, 4)
|
|
|
|
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
|
|
|
|
log.Printf("Failed to read message length: %v", err)
|
2025-02-14 13:35:09 +01:00
|
|
|
continue
|
2025-01-23 09:42:42 +01:00
|
|
|
}
|
|
|
|
messageLength := binary.BigEndian.Uint32(lengthBuf)
|
2025-01-15 10:50:51 +01:00
|
|
|
|
2025-02-14 13:35:09 +01:00
|
|
|
// Read message body
|
2025-01-23 09:42:42 +01:00
|
|
|
messageBuf := make([]byte, messageLength)
|
|
|
|
if _, err := io.ReadFull(reader, messageBuf); err != nil {
|
|
|
|
log.Printf("Failed to read message body: %v", err)
|
2025-02-14 13:35:09 +01:00
|
|
|
continue
|
2025-01-23 09:42:42 +01:00
|
|
|
}
|
2024-10-31 01:06:39 +01:00
|
|
|
|
2025-02-14 13:35:09 +01:00
|
|
|
// Process server message
|
2025-01-23 09:42:42 +01:00
|
|
|
var serverMessage pb.ServerMessage
|
|
|
|
if err := proto.Unmarshal(messageBuf, &serverMessage); err != nil {
|
|
|
|
log.Printf("Failed to unmarshal server message: %v", err)
|
|
|
|
continue
|
|
|
|
}
|
2024-10-31 01:06:39 +01:00
|
|
|
|
2025-02-14 13:35:09 +01:00
|
|
|
// Update player states
|
2025-01-23 09:42:42 +01:00
|
|
|
for _, state := range serverMessage.Players {
|
2025-02-14 13:35:09 +01:00
|
|
|
if state == nil {
|
|
|
|
logging.Error.Println("Received nil player state")
|
2025-01-23 09:42:42 +01:00
|
|
|
continue
|
2025-01-13 10:00:23 +01:00
|
|
|
}
|
2025-01-13 00:31:15 +01:00
|
|
|
|
2025-02-14 13:35:09 +01:00
|
|
|
if state.PlayerId == playerID {
|
|
|
|
player.UpdatePosition(state, types.ServerTickRate)
|
|
|
|
} else if existing, ok := otherPlayers.Load(state.PlayerId); ok {
|
|
|
|
existing.(*types.Player).UpdatePosition(state, types.ServerTickRate)
|
2025-01-23 09:42:42 +01:00
|
|
|
} else {
|
2025-02-14 13:35:09 +01:00
|
|
|
newPlayer := types.NewPlayer(state)
|
|
|
|
otherPlayers.Store(state.PlayerId, newPlayer)
|
2025-01-20 00:03:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-14 13:35:09 +01:00
|
|
|
// Handle chat messages
|
|
|
|
if handler, ok := player.UserData.(types.ChatMessageHandler); ok {
|
2025-01-23 09:42:42 +01:00
|
|
|
handler.HandleServerMessages(serverMessage.ChatMessages)
|
|
|
|
}
|
2024-10-31 01:06:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2025-01-15 10:50:51 +01:00
|
|
|
|
|
|
|
// Helper function to write length-prefixed messages
|
|
|
|
func writeMessage(conn net.Conn, msg proto.Message) error {
|
|
|
|
data, err := proto.Marshal(msg)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write length prefix
|
|
|
|
lengthBuf := make([]byte, 4)
|
|
|
|
binary.BigEndian.PutUint32(lengthBuf, uint32(len(data)))
|
|
|
|
if _, err := conn.Write(lengthBuf); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write message body
|
|
|
|
_, err = conn.Write(data)
|
|
|
|
return err
|
|
|
|
}
|