GOingTunneling/serverboundpacket.go

237 lines
7.4 KiB
Go

package main
import (
"encoding/binary"
"fmt"
"github.com/veandco/go-sdl2/sdl"
"log"
"net"
"sync"
)
func sendPlayerStartRequest(major, minor, patch uint8, conn *net.TCPConn) error {
data := make([]byte, 3)
data[0] = major
data[1] = minor
data[2] = patch
return sendPacket(0, data, conn)
}
func receivePlayerStartRequest(data []byte) (major, minor, patch uint8, err error) {
if len(data) < 3 {
return 0, 0, 0, fmt.Errorf("insufficient data length, got %d instead of 3", len(data))
}
major = data[0]
minor = data[1]
patch = data[2]
return major, minor, patch, nil
}
func sendPlayerLocation(posX, posY uint32, orientation uint8, conn *net.TCPConn) error {
data := make([]byte, 9)
binary.LittleEndian.PutUint32(data[0:4], posX)
binary.LittleEndian.PutUint32(data[4:8], posY)
data[8] = orientation
return sendPacket(1, data, conn)
}
func receivePlayerLocation(data []byte) (posX, posY uint32, orientation uint8, err error) {
if len(data) < 9 {
return 0, 0, 0, fmt.Errorf("insufficient data length, got %d instead of 9", len(data))
}
posX = binary.LittleEndian.Uint32(data[0:4])
posY = binary.LittleEndian.Uint32(data[4:8])
orientation = data[8]
return posX, posY, orientation, nil
}
func sendDigBlock(posX, posY uint32, isShooting bool, conn *net.TCPConn) error {
data := make([]byte, 9)
binary.LittleEndian.PutUint32(data[0:4], posX)
binary.LittleEndian.PutUint32(data[4:8], posY)
data[8] = boolToByte(isShooting)
return sendPacket(2, data, conn)
}
func receiveDigBlock(data []byte) (posX, posY uint32, isShooting bool, err error) {
if len(data) < 9 {
return 0, 0, false, fmt.Errorf("insufficient data length, got %d instead of 9", len(data))
}
posX = binary.LittleEndian.Uint32(data[0:4])
posY = binary.LittleEndian.Uint32(data[4:8])
isShooting = byteToBool(data[8])
return posX, posY, isShooting, nil
}
func sendShoot(isSuper bool, conn *net.TCPConn) error {
data := make([]byte, 1)
data[0] = boolToByte(isSuper)
return sendPacket(3, data, conn)
}
func receiveShoot(data []byte) (isSuper bool, err error) {
if len(data) < 1 {
return false, fmt.Errorf("insufficient data length, got %d instead of 1", len(data))
}
isSuper = byteToBool(data[0])
return isSuper, nil
}
func handleRequest(conn *net.TCPConn, players map[uint32]*Player, bullets map[uint32]*Bullet, bases map[uint32]*Base) {
defer func(conn *net.TCPConn) {
_ = conn.Close()
}(conn)
for {
packetID, _, packetData, err := receivePacket(conn)
if err != nil {
return
}
switch packetID {
//goland:noinspection GoDeferInLoop,GoDeferInLoop
case 0:
versionArray := getCurrentVersion()
major, minor, patch, err := receivePlayerStartRequest(packetData)
if err != nil {
continue
}
if versionArray[0] != major || versionArray[1] != minor || versionArray[2] != patch {
log.Printf("Incorrect version tried to connect with %d.%d.%d from %s to %d.%d.%d", versionArray[0], versionArray[1], versionArray[2], conn.RemoteAddr().String(), major, minor, patch)
return
}
log.Printf("Connected from %s with %d.%d.%d", conn.RemoteAddr().String(), major, minor, patch)
unusedColor := getUnusedColor(playerColors, players)
newPlayerID := createPlayer(false, playerColors[unusedColor], unusedColor, conn, KeyMap{}, JoyMap{}, nil, gameMap, players, bases)
defer delete(players, newPlayerID)
playersMutex.RLock()
player := players[newPlayerID]
playersMutex.RUnlock()
baseMutex.Lock()
bases[newPlayerID] = createBase(gameMap,
player.playerColors.body,
uint32(player.gameObject.baseRect.X-14),
uint32(player.gameObject.baseRect.Y-14),
player.playerID,
uint32(float64(player.gameObject.baseRect.W)*1.5))
baseMutex.Unlock()
defer bases[newPlayerID].delete(bases)
netPlayerMapper[conn] = player
success := player.sendVersionBackToPlayer()
if !success {
return
}
log.Printf("Sent version to %d", newPlayerID)
success = player.sendServerInfoToPlayer()
if !success {
log.Printf("Server info not sent to %d", newPlayerID)
return
}
log.Printf("Sent server info to %d", newPlayerID)
var wgX sync.WaitGroup
wgX.Add(1)
success = player.sendPlayerUpdate(&wgX)
if !success {
log.Printf("Server info not sent to %d", newPlayerID)
return
}
log.Printf("Sent info to %d", newPlayerID)
success = player.sendLocationToPlayer()
if !success {
log.Printf("Server location not sent to %d", newPlayerID)
return
}
log.Printf("Sent location to %d", newPlayerID)
player.initialized = true
case 1:
player := netPlayerMapper[conn]
if player == nil {
log.Fatalf("Can't find player for connection")
}
newX, newY, orientation, err := receivePlayerLocation(packetData)
if err != nil {
log.Printf("Error receiving player location: %s", err)
}
oldX := uint32(player.gameObject.baseRect.X)
oldY := uint32(player.gameObject.baseRect.Y)
// Update player orientation
player.gameObject.orientation = orientation
player.gameObject.adjustBaseRect()
// Check if the new position is adjacent to the old position
isAdjacentX := (newX > oldX && newX-oldX <= 1) || (newX < oldX && oldX-newX <= 1) || newX == oldX
isAdjacentY := (newY > oldY && newY-oldY <= 1) || (newY < oldY && oldY-newY <= 1) || newY == oldY
// Ensure the player is not moving while on cooldown and is moving to an adjacent position
if isAdjacentX && isAdjacentY && player.movementCooldown == 0 {
// Terrain collision check: Loop over all tiles in the new bounding box area
canMove := true
for x := newX; x < newX+uint32(player.gameObject.baseRect.W); x++ {
for y := newY; y < newY+uint32(player.gameObject.baseRect.H); y++ {
if gameMap.tiles[x][y] != 0 { // Assumes 0 is passable, any other value is impassable
player.knownGameMap.tiles[x][y] = 0
canMove = false
}
}
if !canMove {
break
}
}
// If all tiles in the area are passable, update the player's position
if canMove {
player.gameObject.baseRect = &sdl.Rect{
X: int32(newX),
Y: int32(newY),
W: player.gameObject.baseRect.W,
H: player.gameObject.baseRect.H,
}
// Deduct energy if the player has moved
if newX != oldX || newY != oldY {
if player.energy > serverConfig.MovementCost {
player.energy -= serverConfig.MovementCost
player.movementCooldown = serverConfig.MovementCooldown
} else {
player.movementCooldown = serverConfig.MovementCooldownNoEnergy
}
}
} else {
// If any part of the new area is impassable, revert to the old position
player.sendLocationToPlayer()
}
} else {
// If the movement is invalid (not adjacent or on cooldown), revert to the old position
player.sendLocationToPlayer()
}
case 2:
player := netPlayerMapper[conn]
if player == nil {
log.Fatalf("Can't find player for connection")
}
posX, posY, isShooting, err := receiveDigBlock(packetData)
if err != nil {
log.Printf("Error receiving dig block: %s", err)
}
res := player.digBlock(posX, posY, gameMap, isShooting)
if res == 1 {
if player.energy > serverConfig.DiggingCost {
player.energy -= serverConfig.DiggingCost
}
}
case 3:
player := netPlayerMapper[conn]
if player == nil {
log.Fatalf("Can't find player for connection")
}
isSuper, err := receiveShoot(packetData)
if err != nil {
log.Printf("Error receiving dig block: %s", err)
}
player.shoot(isSuper, bullets)
}
}
}