GOingTunneling/clientboundpacket.go

466 lines
18 KiB
Go
Raw Permalink Normal View History

2024-09-01 20:13:53 +02:00
package main
import (
"encoding/binary"
"fmt"
"image/color"
"log"
"net"
"os"
)
func sendPlayerStartResponse(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 receivePlayerStartResponse(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 sendServerInfo(
playerID, playerColorID, maxEnergy, maxAmmunition, maxShields, mapWidth, mapHeight, normalShotCost,
superShotCost, reloadCost, movementCost, diggingCost, shootDiggingBonus, shootCooldown, rechargeCooldown,
rechargeOpponentCooldown, repairCooldown, diggingCooldown, movementCooldown, movementCooldownNoEnergy,
diggingCooldownNoEnergy, reloadCooldown uint32, blastRadius uint8, reloadWait uint32, conn *net.TCPConn) error {
data := make([]byte, 93) // Adjusted size to accommodate playerID and playerColorID
binary.LittleEndian.PutUint32(data[0:4], playerID)
binary.LittleEndian.PutUint32(data[4:8], playerColorID)
binary.LittleEndian.PutUint32(data[8:12], maxEnergy)
binary.LittleEndian.PutUint32(data[12:16], maxAmmunition)
binary.LittleEndian.PutUint32(data[16:20], maxShields)
binary.LittleEndian.PutUint32(data[20:24], mapWidth)
binary.LittleEndian.PutUint32(data[24:28], mapHeight)
binary.LittleEndian.PutUint32(data[28:32], normalShotCost)
binary.LittleEndian.PutUint32(data[32:36], superShotCost)
binary.LittleEndian.PutUint32(data[36:40], reloadCost)
binary.LittleEndian.PutUint32(data[40:44], movementCost)
binary.LittleEndian.PutUint32(data[44:48], diggingCost)
binary.LittleEndian.PutUint32(data[48:52], shootDiggingBonus)
binary.LittleEndian.PutUint32(data[52:56], shootCooldown)
binary.LittleEndian.PutUint32(data[56:60], rechargeCooldown)
binary.LittleEndian.PutUint32(data[60:64], rechargeOpponentCooldown)
binary.LittleEndian.PutUint32(data[64:68], repairCooldown)
binary.LittleEndian.PutUint32(data[68:72], diggingCooldown)
binary.LittleEndian.PutUint32(data[72:76], movementCooldown)
binary.LittleEndian.PutUint32(data[76:80], movementCooldownNoEnergy)
binary.LittleEndian.PutUint32(data[80:84], diggingCooldownNoEnergy)
binary.LittleEndian.PutUint32(data[84:88], reloadCooldown)
data[88] = blastRadius
binary.LittleEndian.PutUint32(data[89:93], reloadWait)
return sendPacket(5, data, conn)
}
func receiveServerInfo(data []byte) (
playerID, playerColorID, maxEnergy, maxAmmunition, maxShields, mapWidth, mapHeight, normalShotCost, superShotCost,
reloadCost, movementCost, diggingCost, shootDiggingBonus, shootCooldown, rechargeCooldown, rechargeOpponentCooldown,
repairCooldown, diggingCooldown, movementCooldown, movementCooldownNoEnergy, diggingCooldownNoEnergy, reloadCooldown uint32,
blastRadius uint8, reloadWait uint32, err error) {
if len(data) < 93 {
return 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, fmt.Errorf("insufficient data length, got %d instead of 93", len(data))
}
playerID = binary.LittleEndian.Uint32(data[0:4])
playerColorID = binary.LittleEndian.Uint32(data[4:8])
maxEnergy = binary.LittleEndian.Uint32(data[8:12])
maxAmmunition = binary.LittleEndian.Uint32(data[12:16])
maxShields = binary.LittleEndian.Uint32(data[16:20])
mapWidth = binary.LittleEndian.Uint32(data[20:24])
mapHeight = binary.LittleEndian.Uint32(data[24:28])
normalShotCost = binary.LittleEndian.Uint32(data[28:32])
superShotCost = binary.LittleEndian.Uint32(data[32:36])
reloadCost = binary.LittleEndian.Uint32(data[36:40])
movementCost = binary.LittleEndian.Uint32(data[40:44])
diggingCost = binary.LittleEndian.Uint32(data[44:48])
shootDiggingBonus = binary.LittleEndian.Uint32(data[48:52])
shootCooldown = binary.LittleEndian.Uint32(data[52:56])
rechargeCooldown = binary.LittleEndian.Uint32(data[56:60])
rechargeOpponentCooldown = binary.LittleEndian.Uint32(data[60:64])
repairCooldown = binary.LittleEndian.Uint32(data[64:68])
diggingCooldown = binary.LittleEndian.Uint32(data[68:72])
movementCooldown = binary.LittleEndian.Uint32(data[72:76])
movementCooldownNoEnergy = binary.LittleEndian.Uint32(data[76:80])
diggingCooldownNoEnergy = binary.LittleEndian.Uint32(data[80:84])
reloadCooldown = binary.LittleEndian.Uint32(data[84:88])
blastRadius = data[88]
reloadWait = binary.LittleEndian.Uint32(data[89:93])
return playerID, playerColorID, maxEnergy, maxAmmunition, maxShields, mapWidth, mapHeight, normalShotCost, superShotCost,
reloadCost, movementCost, diggingCost, shootDiggingBonus, shootCooldown, rechargeCooldown, rechargeOpponentCooldown,
repairCooldown, diggingCooldown, movementCooldown, movementCooldownNoEnergy, diggingCooldownNoEnergy, reloadCooldown,
blastRadius, reloadWait, nil
}
func sendPositionUpdate(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 receivePositionUpdate(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 sendOtherPlayer(playerID, posX, posY uint32, orientation uint8, conn *net.TCPConn) error {
data := make([]byte, 13)
binary.LittleEndian.PutUint32(data[0:4], playerID)
binary.LittleEndian.PutUint32(data[4:8], posX)
binary.LittleEndian.PutUint32(data[8:12], posY)
data[12] = orientation
return sendPacket(2, data, conn)
}
func receiveOtherPlayer(data []byte) (playerID, posX, posY uint32, orientation uint8, err error) {
if len(data) < 13 {
return 0, 0, 0, 0, fmt.Errorf("insufficient data length, got %d instead of 13", len(data))
}
playerID = binary.LittleEndian.Uint32(data[0:4])
posX = binary.LittleEndian.Uint32(data[4:8])
posY = binary.LittleEndian.Uint32(data[8:12])
orientation = data[12]
return playerID, posX, posY, orientation, nil
}
func sendBullet(posX, posY uint32, direction, red, green, blue, alpha uint8, bulletID uint32, isSuper bool, ownerID uint32, conn *net.TCPConn) error {
data := make([]byte, 22)
binary.LittleEndian.PutUint32(data[0:4], posX)
binary.LittleEndian.PutUint32(data[4:8], posY)
data[8] = direction
data[9] = red
data[10] = green
data[11] = blue
data[12] = alpha
binary.LittleEndian.PutUint32(data[13:17], bulletID)
data[17] = boolToByte(isSuper)
binary.LittleEndian.PutUint32(data[18:22], ownerID)
return sendPacket(3, data, conn)
}
func receiveBullet(data []byte) (posX, posY uint32, direction, red, green, blue, alpha uint8, bulletID uint32, isSuper bool, ownerID uint32, err error) {
if len(data) < 22 {
return 0, 0, 0, 0, 0, 0, 0, 0, false, 0, fmt.Errorf("insufficient data length, got %d instead of 21", len(data))
}
posX = binary.LittleEndian.Uint32(data[0:4])
posY = binary.LittleEndian.Uint32(data[4:8])
direction = data[8]
red = data[9]
green = data[10]
blue = data[11]
alpha = data[12]
bulletID = binary.LittleEndian.Uint32(data[13:17])
isSuper = byteToBool(data[17])
ownerID = binary.LittleEndian.Uint32(data[18:22])
return posX, posY, direction, red, green, blue, alpha, bulletID, isSuper, ownerID, nil
}
func sendBulletParticle(posX, posY, expirationTimer uint32, red, green, blue, alpha uint8, bulletParticleID uint32, conn *net.TCPConn) error {
data := make([]byte, 20)
binary.LittleEndian.PutUint32(data[0:4], posX)
binary.LittleEndian.PutUint32(data[4:8], posY)
binary.LittleEndian.PutUint32(data[8:12], expirationTimer)
data[12] = red
data[13] = green
data[14] = blue
data[15] = alpha
binary.LittleEndian.PutUint32(data[16:20], bulletParticleID)
return sendPacket(4, data, conn)
}
func receiveBulletParticle(data []byte) (posX, posY, expirationTimer uint32, red, green, blue, alpha uint8, bulletParticleID uint32, err error) {
if len(data) < 20 {
return 0, 0, 0, 0, 0, 0, 0, 0, fmt.Errorf("insufficient data length, got %d instead of 20", len(data))
}
posX = binary.LittleEndian.Uint32(data[0:4])
posY = binary.LittleEndian.Uint32(data[4:8])
expirationTimer = binary.LittleEndian.Uint32(data[8:12])
red = data[12]
green = data[13]
blue = data[14]
alpha = data[15]
bulletParticleID = binary.LittleEndian.Uint32(data[16:20])
return posX, posY, expirationTimer, red, green, blue, alpha, bulletParticleID, nil
}
func sendPlayerUpdate(energy, ammo, shields uint32, conn *net.TCPConn) error {
data := make([]byte, 12)
binary.LittleEndian.PutUint32(data[0:4], energy)
binary.LittleEndian.PutUint32(data[4:8], ammo)
binary.LittleEndian.PutUint32(data[8:12], shields)
return sendPacket(6, data, conn)
}
func receivePlayerUpdate(data []byte) (energy, ammo, shields uint32, err error) {
if len(data) < 12 {
return 0, 0, 0, fmt.Errorf("insufficient data length, got %d instead of 12", len(data))
}
energy = binary.LittleEndian.Uint32(data[0:4])
ammo = binary.LittleEndian.Uint32(data[4:8])
shields = binary.LittleEndian.Uint32(data[8:12])
return energy, ammo, shields, nil
}
func sendBaseLocation(posX, posY uint32, ownerID, playerColorID uint32, conn *net.TCPConn) error {
data := make([]byte, 16)
binary.LittleEndian.PutUint32(data[0:4], posX)
binary.LittleEndian.PutUint32(data[4:8], posY)
binary.LittleEndian.PutUint32(data[8:12], ownerID)
binary.LittleEndian.PutUint32(data[12:16], playerColorID)
return sendPacket(7, data, conn)
}
func receiveBaseLocation(data []byte) (posX, posY uint32, ownerID, playerColorID uint32, err error) {
if len(data) < 16 {
return 0, 0, 0, 0, fmt.Errorf("insufficient data length, got %d instead of 16", len(data))
}
posX = binary.LittleEndian.Uint32(data[0:4])
posY = binary.LittleEndian.Uint32(data[4:8])
ownerID = binary.LittleEndian.Uint32(data[8:12])
playerColorID = binary.LittleEndian.Uint32(data[12:16])
return posX, posY, ownerID, playerColorID, nil
}
func sendTileUpdate(posX, posY uint32, kind 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] = kind
return sendPacket(8, data, conn)
}
func receiveTileUpdate(data []byte) (posX, posY uint32, kind 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])
kind = data[8]
return posX, posY, kind, nil
}
func handleConnectionClient(conn *net.TCPConn, players map[uint32]*Player, bases map[uint32]*Base, bullets map[uint32]*Bullet, bulletParticles map[uint32]*BulletParticle) {
var player *Player
sendVersionToServer(conn)
for {
packetID, _, packetData, err := receivePacket(conn)
if err != nil {
log.Printf("Error receiving packet: %v", err)
os.Exit(0)
return
}
switch packetID {
case 0:
major, minor, patch, err := receivePlayerStartResponse(packetData)
if err != nil {
log.Printf("Error receiving player start response: %v", err)
}
versionArray := getCurrentVersion()
if versionArray[0] != major || versionArray[1] != minor || versionArray[2] != patch {
log.Fatalf("Wrong version tried to connect with %d.%d.%d to %d.%d.%d", major, minor, patch, versionArray[0], versionArray[1], versionArray[2])
}
case 1:
if clientInitialized {
posX, posY, orientation, err := receivePositionUpdate(packetData)
if err != nil {
log.Printf("Error receiving player location: %v", err)
}
player.gameObject.baseRect.X = int32(posX)
player.gameObject.baseRect.Y = int32(posY)
player.gameObject.orientation = orientation
player.gameObject.adjustBaseRect()
}
case 2:
playerID, posX, posY, orientation, err := receiveOtherPlayer(packetData)
if err != nil {
log.Printf("Error receiving player response: %v", err)
}
playersMutex.RLock()
existingPlayer := players[playerID]
playersMutex.RUnlock()
if existingPlayer == nil {
lastPlayerID = playerID
playerColor := playerID % uint32(len(playerColors))
createPlayer(false, playerColors[playerColor], playerColor, nil, KeyMap{}, JoyMap{}, nil, gameMap, players, bases)
playersMutex.RLock()
newPlayer := players[playerID]
playersMutex.RUnlock()
newPlayer.gameObject.orientation = orientation
newPlayer.gameObject.adjustBaseRect()
newPlayer.gameObject.baseRect.X = int32(posX)
newPlayer.gameObject.baseRect.Y = int32(posY)
} else {
existingPlayer.gameObject.orientation = orientation
existingPlayer.gameObject.adjustBaseRect()
existingPlayer.gameObject.baseRect.X = int32(posX)
existingPlayer.gameObject.baseRect.Y = int32(posY)
}
case 3:
posX, posY, direction, red, green, blue, alpha, bulletID, isSuper, ownerID, err := receiveBullet(packetData)
if err != nil {
log.Printf("Error receiving bullet response: %v", err)
}
inColor := color.RGBA{
R: red,
G: green,
B: blue,
A: alpha,
}
existingBullet := bullets[bulletID]
if existingBullet == nil {
bulletMutex.Lock()
bullets[bulletID] = &Bullet{
posX: int32(posX),
posY: int32(posY),
direction: direction,
color: inColor,
super: isSuper,
id: bulletID,
ownerID: ownerID,
}
bulletMutex.Unlock()
} else {
existingBullet.direction = direction
existingBullet.color = inColor
existingBullet.super = isSuper
existingBullet.posX = int32(posX)
existingBullet.posY = int32(posY)
}
case 4:
posX, posY, expirationTimer, red, green, blue, alpha, bulletParticleID, err := receiveBulletParticle(packetData)
if err != nil {
log.Printf("Error receiving bullet particle: %v", err)
}
inColor := color.RGBA{
R: red,
G: green,
B: blue,
A: alpha,
}
bulletParticleMutex.Lock()
existingBulletParticle := bulletParticles[bulletParticleID]
if existingBulletParticle == nil {
bulletParticles[bulletParticleID] = &BulletParticle{
posX: int32(posX),
posY: int32(posY),
expirationTimer: expirationTimer,
color: inColor,
id: bulletParticleID,
}
} else {
existingBulletParticle.color = inColor
existingBulletParticle.posX = int32(posX)
existingBulletParticle.posY = int32(posY)
existingBulletParticle.expirationTimer = expirationTimer
}
bulletParticleMutex.Unlock()
case 5:
playerID, playerColorID, maxEnergy, maxAmmunition, maxShields, mapWidth, mapHeight, normalShotCost, superShotCost, reloadCost, movementCost,
diggingCost, shootDiggingBonus, shootCooldown, rechargeCooldown, rechargeOpponentCooldown, repairCooldown, diggingCooldown, movementCooldown,
movementCooldownNoEnergy, diggingCooldownNoEnergy, reloadCooldown, blastRadius, reloadWait, err := receiveServerInfo(packetData)
if err != nil {
log.Printf("Error receiving server info: %v", err)
}
serverConfig = ServerConfig{
MapWidth: mapWidth,
MapHeight: mapHeight,
BlastRadius: blastRadius,
MaxEnergy: maxEnergy,
MaxAmmunition: maxAmmunition,
MaxShields: maxShields,
NormalShotCost: normalShotCost,
SuperShotCost: superShotCost,
ReloadCost: reloadCost,
MovementCost: movementCost,
DiggingCost: diggingCost,
ShootDiggingCostBonus: shootDiggingBonus,
ShootCooldown: shootCooldown,
RechargeCooldownOwn: rechargeCooldown,
DiggingCooldown: diggingCooldown,
RechargeCooldownOpponent: rechargeOpponentCooldown,
RepairCooldown: repairCooldown,
MovementCooldown: movementCooldown,
MovementCooldownNoEnergy: movementCooldownNoEnergy,
DiggingCooldownNoEnergy: diggingCooldownNoEnergy,
ReloadCooldown: reloadCooldown,
ReloadWait: reloadWait,
}
gameMap.createGameMap(false)
lastPlayerID = playerID
createPlayer(true, playerColors[playerColorID], playerColorID, conn, keyMaps[config.KeyBindOffset], JoyMap{}, nil, gameMap, players, bases)
playersMutex.RLock()
player = players[playerID]
playersMutex.RUnlock()
log.Printf("Got server info, now initializing\n")
clientInitialized = true
case 6:
if clientInitialized {
energy, ammo, shields, err := receivePlayerUpdate(packetData)
if err != nil {
log.Printf("Error receiving player update: %v", err)
}
player.energy = energy
player.ammunition = ammo
player.shields = shields
}
case 7:
posX, posY, ownerID, playerColorID, err := receiveBaseLocation(packetData)
if err != nil {
log.Printf("Error receiving base location: %v", err)
}
baseID := ownerID
baseMutex.RLock()
existingBase := bases[baseID]
baseMutex.RUnlock()
inColor := playerColors[playerColorID].body
if existingBase == nil {
baseMutex.Lock()
bases[baseID] = createBase(gameMap,
inColor,
posX,
posY,
ownerID,
uint32(float64(player.gameObject.baseRect.W)*1.5),
)
baseMutex.Unlock()
} else {
existingBase.gameObject.baseRect.X = int32(posX)
existingBase.gameObject.baseRect.Y = int32(posY)
existingBase.gameObject.colors[0] = inColor
}
case 8:
posX, posY, kind, err := receiveTileUpdate(packetData)
if err != nil {
log.Printf("Error receiving tile update: %v", err)
}
if posX > 0 && posX < gameMap.width && posY > 0 && posY < gameMap.height {
gameMap.tiles[posX][posY] = kind
}
}
}
}