237 lines
7.4 KiB
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)
|
|
}
|
|
}
|
|
}
|