191 lines
4.7 KiB
Go
191 lines
4.7 KiB
Go
package network
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/binary"
|
|
"io"
|
|
"log"
|
|
"net"
|
|
"time"
|
|
|
|
"gitea.boner.be/bdnugget/goonscape/game"
|
|
"gitea.boner.be/bdnugget/goonscape/types"
|
|
pb "gitea.boner.be/bdnugget/goonserver/actions"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
var serverAddr = "boner.be:6969"
|
|
|
|
func SetServerAddr(addr string) {
|
|
serverAddr = addr
|
|
}
|
|
|
|
func ConnectToServer() (net.Conn, int32, error) {
|
|
conn, err := net.Dial("tcp", serverAddr)
|
|
if err != nil {
|
|
log.Printf("Failed to dial server: %v", err)
|
|
return nil, 0, err
|
|
}
|
|
|
|
log.Println("Connected to server. Waiting for player ID...")
|
|
reader := bufio.NewReader(conn)
|
|
|
|
// Read message length (4 bytes)
|
|
lengthBuf := make([]byte, 4)
|
|
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
|
|
log.Printf("Failed to read message length: %v", err)
|
|
return nil, 0, err
|
|
}
|
|
messageLength := binary.BigEndian.Uint32(lengthBuf)
|
|
|
|
// Read the full message
|
|
messageBuf := make([]byte, messageLength)
|
|
if _, err := io.ReadFull(reader, messageBuf); err != nil {
|
|
log.Printf("Failed to read message body: %v", err)
|
|
return nil, 0, err
|
|
}
|
|
|
|
var response pb.ServerMessage
|
|
if err := proto.Unmarshal(messageBuf, &response); err != nil {
|
|
log.Printf("Failed to unmarshal server response: %v", err)
|
|
return nil, 0, err
|
|
}
|
|
|
|
playerID := response.GetPlayerId()
|
|
log.Printf("Successfully connected with player ID: %d", playerID)
|
|
return conn, playerID, nil
|
|
}
|
|
|
|
func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Player, otherPlayers map[int32]*types.Player, quitChan <-chan struct{}) {
|
|
reader := bufio.NewReader(conn)
|
|
|
|
actionTicker := time.NewTicker(types.ClientTickRate)
|
|
defer actionTicker.Stop()
|
|
defer conn.Close()
|
|
defer close(player.QuitDone)
|
|
|
|
// Create a channel to signal when goroutines are done
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-quitChan:
|
|
// Send disconnect message to server
|
|
disconnectMsg := &pb.ActionBatch{
|
|
PlayerId: playerID,
|
|
Actions: []*pb.Action{{
|
|
Type: pb.Action_DISCONNECT,
|
|
PlayerId: playerID,
|
|
}},
|
|
}
|
|
writeMessage(conn, disconnectMsg)
|
|
done <- struct{}{}
|
|
return
|
|
case <-actionTicker.C:
|
|
player.Lock()
|
|
if len(player.ActionQueue) > 0 {
|
|
actions := make([]*pb.Action, len(player.ActionQueue))
|
|
copy(actions, player.ActionQueue)
|
|
|
|
batch := &pb.ActionBatch{
|
|
PlayerId: playerID,
|
|
Actions: actions,
|
|
Tick: player.CurrentTick,
|
|
}
|
|
|
|
player.ActionQueue = player.ActionQueue[:0]
|
|
player.Unlock()
|
|
|
|
if err := writeMessage(conn, batch); err != nil {
|
|
log.Printf("Failed to send actions to server: %v", err)
|
|
return
|
|
}
|
|
} else {
|
|
player.Unlock()
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case <-quitChan:
|
|
<-done // Wait for action goroutine to finish
|
|
close(done)
|
|
time.Sleep(100 * time.Millisecond) // Give time for disconnect message to be sent
|
|
return
|
|
default:
|
|
// Read message length (4 bytes)
|
|
lengthBuf := make([]byte, 4)
|
|
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
|
|
log.Printf("Failed to read message length: %v", err)
|
|
return
|
|
}
|
|
messageLength := binary.BigEndian.Uint32(lengthBuf)
|
|
|
|
// Read the full message
|
|
messageBuf := make([]byte, messageLength)
|
|
if _, err := io.ReadFull(reader, messageBuf); err != nil {
|
|
log.Printf("Failed to read message body: %v", err)
|
|
return
|
|
}
|
|
|
|
var serverMessage pb.ServerMessage
|
|
if err := proto.Unmarshal(messageBuf, &serverMessage); err != nil {
|
|
log.Printf("Failed to unmarshal server message: %v", err)
|
|
continue
|
|
}
|
|
|
|
player.Lock()
|
|
player.CurrentTick = serverMessage.CurrentTick
|
|
|
|
tickDiff := serverMessage.CurrentTick - player.CurrentTick
|
|
if tickDiff > types.MaxTickDesync {
|
|
for _, state := range serverMessage.Players {
|
|
if state.PlayerId == playerID {
|
|
player.ForceResync(state)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
player.Unlock()
|
|
|
|
for _, state := range serverMessage.Players {
|
|
if state.PlayerId == playerID {
|
|
continue
|
|
}
|
|
|
|
if otherPlayer, exists := otherPlayers[state.PlayerId]; exists {
|
|
otherPlayer.UpdatePosition(state, types.ServerTickRate)
|
|
} else {
|
|
otherPlayers[state.PlayerId] = types.NewPlayer(state)
|
|
}
|
|
}
|
|
|
|
if g, ok := player.UserData.(*game.Game); ok && len(serverMessage.ChatMessages) > 0 {
|
|
g.Chat.HandleServerMessages(serverMessage.ChatMessages)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|