Update networking stack
This commit is contained in:
parent
b3c789303b
commit
589fd61902
@ -181,7 +181,9 @@ func (bullet *Bullet) tick(gameMap *GameMap,
|
||||
}
|
||||
playersMutex.RUnlock()
|
||||
if collisionResult != 0 || hitPlayer {
|
||||
bullet.explode(gameMap, bulletParticleMap)
|
||||
if !config.Client {
|
||||
bullet.explode(gameMap, bulletParticleMap)
|
||||
}
|
||||
delete(bulletMap, bullet.id)
|
||||
} else {
|
||||
bullet.posX = nextX
|
||||
|
465
clientboundpacket.go
Normal file
465
clientboundpacket.go
Normal file
@ -0,0 +1,465 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -76,16 +76,16 @@ func (gameObject *GameObject) render(camera *sdl.Rect, surface *sdl.Surface) {
|
||||
gameObject.inView = true
|
||||
if config.Debug {
|
||||
baseRectFinal := adjustRectToCamera(gameObject.baseRect, camera)
|
||||
surface.FillRect(baseRectFinal, sdl.MapRGBA(surface.Format, 255, 20, 10, 64))
|
||||
_ = surface.FillRect(baseRectFinal, sdl.MapRGBA(surface.Format, 255, 20, 10, 64))
|
||||
if !gameObject.collisionRect.Empty() {
|
||||
surface.FillRect(adjustRectToCamera(gameObject.collisionRect, camera), sdl.MapRGBA(surface.Format, 40, 192, 255, 64))
|
||||
_ = surface.FillRect(adjustRectToCamera(gameObject.collisionRect, camera), sdl.MapRGBA(surface.Format, 40, 192, 255, 64))
|
||||
}
|
||||
}
|
||||
if config.RenderGameObjects {
|
||||
for _, coloredRect := range gameObject.visualRects[gameObject.orientation] {
|
||||
finalRect := gameObject.adjustRectToCamera(coloredRect.rect, camera)
|
||||
r, g, b, a := coloredRect.color.RGBA()
|
||||
surface.FillRect(finalRect, sdl.MapRGBA(surface.Format, uint8(r), uint8(g), uint8(b), uint8(a)))
|
||||
_ = surface.FillRect(finalRect, sdl.MapRGBA(surface.Format, uint8(r), uint8(g), uint8(b), uint8(a)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
1
go.mod
1
go.mod
@ -5,5 +5,4 @@ go 1.21
|
||||
require (
|
||||
github.com/aquilax/go-perlin v1.1.0
|
||||
github.com/veandco/go-sdl2 v0.4.40
|
||||
google.golang.org/protobuf v1.34.2
|
||||
)
|
||||
|
10
graphics.go
10
graphics.go
@ -99,8 +99,8 @@ func handleWindowResize(window *sdl.Window, logicalSurface *sdl.Surface) {
|
||||
srcRect := &sdl.Rect{X: 0, Y: 0, W: logicalSurface.W, H: logicalSurface.H}
|
||||
dstRect := &sdl.Rect{X: letterboxX, Y: letterboxY, W: newWidth, H: newHeight}
|
||||
|
||||
logicalSurface.BlitScaled(srcRect, windowSurface, dstRect)
|
||||
window.UpdateSurface()
|
||||
_ = logicalSurface.BlitScaled(srcRect, windowSurface, dstRect)
|
||||
_ = window.UpdateSurface()
|
||||
}
|
||||
|
||||
func adjustWindow(window *sdl.Window, logicalSurface *sdl.Surface) {
|
||||
@ -133,15 +133,15 @@ func adjustWindow(window *sdl.Window, logicalSurface *sdl.Surface) {
|
||||
letterboxY := (windowHeight - newHeight) / 2
|
||||
|
||||
// Fill background
|
||||
windowSurface.FillRect(nil, sdl.MapRGBA(logicalSurface.Format, 80, 20, 10, 255))
|
||||
_ = windowSurface.FillRect(nil, sdl.MapRGBA(logicalSurface.Format, 80, 20, 10, 255))
|
||||
|
||||
// Set source and destination rectangles
|
||||
srcRect := &sdl.Rect{X: 0, Y: 0, W: logicalSurface.W, H: logicalSurface.H}
|
||||
dstRect := &sdl.Rect{X: letterboxX, Y: letterboxY, W: newWidth, H: newHeight}
|
||||
|
||||
// Perform the scaled blit
|
||||
logicalSurface.BlitScaled(srcRect, windowSurface, dstRect)
|
||||
_ = logicalSurface.BlitScaled(srcRect, windowSurface, dstRect)
|
||||
|
||||
// Update the window surface
|
||||
window.UpdateSurface()
|
||||
_ = window.UpdateSurface()
|
||||
}
|
||||
|
8
hud.go
8
hud.go
@ -48,13 +48,13 @@ func initHud(surface *sdl.Surface) {
|
||||
sdl.MapRGBA(surface.Format, 40, 243, 243, 255),
|
||||
}
|
||||
HUDColor := sdl.MapRGBA(surface.Format, 101, 101, 101, 255)
|
||||
surface.FillRect(nil, HUDColor)
|
||||
_ = surface.FillRect(nil, HUDColor)
|
||||
|
||||
for letterIndex, letter := range rects {
|
||||
for _, letterRect := range letter {
|
||||
offsetRect := letterRect
|
||||
offsetRect.Y += letterOffsets[letterIndex]
|
||||
surface.FillRect(&offsetRect, letterColors[letterIndex])
|
||||
_ = surface.FillRect(&offsetRect, letterColors[letterIndex])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,7 @@ func initHud(surface *sdl.Surface) {
|
||||
func renderHud(player *Player, surface *sdl.Surface) {
|
||||
|
||||
for _, letterOffset := range letterOffsets {
|
||||
surface.FillRect(&sdl.Rect{X: 16, Y: letterOffset - 1, W: 90, H: 7}, 0)
|
||||
_ = surface.FillRect(&sdl.Rect{X: 16, Y: letterOffset - 1, W: 90, H: 7}, 0)
|
||||
}
|
||||
|
||||
HUDValues := []int32{
|
||||
@ -72,7 +72,7 @@ func renderHud(player *Player, surface *sdl.Surface) {
|
||||
}
|
||||
|
||||
for HUDValueIndex, HUDValue := range HUDValues {
|
||||
surface.FillRect(
|
||||
_ = surface.FillRect(
|
||||
&sdl.Rect{
|
||||
X: 17,
|
||||
Y: letterOffsets[HUDValueIndex],
|
||||
|
74
main.go
74
main.go
@ -2,23 +2,22 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const GameVersion = "TunnEElineningnegbfbf Through the wiAldWest"
|
||||
const GameVersion = "0.0.1"
|
||||
|
||||
type ServerConfig struct {
|
||||
MapWidth uint32 `json:"map_width"`
|
||||
MapHeight uint32 `json:"map_height"`
|
||||
BlastRadius uint32 `json:"blast_radius"`
|
||||
BlastRadius uint8 `json:"blast_radius"`
|
||||
|
||||
MaxEnergy uint32 `json:"max_energy"`
|
||||
MaxAmmunition uint32 `json:"max_ammunition"`
|
||||
@ -120,14 +119,14 @@ func loadOrCreateConfig(filename string) (*Config, error) {
|
||||
return nil, fmt.Errorf("failed to marshal config: %v", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filename, data, 0644)
|
||||
err = os.WriteFile(filename, data, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write config file: %v", err)
|
||||
}
|
||||
fmt.Println("Config file created with default values.")
|
||||
} else {
|
||||
// File exists, load the config
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read config file: %v", err)
|
||||
}
|
||||
@ -172,17 +171,6 @@ func main() {
|
||||
initializeSDL()
|
||||
defer sdl.Quit()
|
||||
}
|
||||
if config.ProfilerOn {
|
||||
f, err := os.Create("cpuprofile")
|
||||
if err != nil {
|
||||
log.Fatal("could not create CPU profile: ", err)
|
||||
}
|
||||
defer f.Close() // error handling omitted for example
|
||||
if err := pprof.StartCPUProfile(f); err != nil {
|
||||
log.Fatal("could not start CPU profile: ", err)
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
mapRendererRect = &sdl.Rect{X: 0, Y: 0, W: int32(serverConfig.MapWidth), H: int32(serverConfig.MapHeight)}
|
||||
players := make(map[uint32]*Player)
|
||||
initPlayerColors()
|
||||
@ -210,7 +198,9 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to server: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
defer func(conn *net.TCPConn) {
|
||||
_ = conn.Close()
|
||||
}(conn)
|
||||
|
||||
go handleConnectionClient(conn, players, bases, bullets, bulletParticles)
|
||||
for !clientInitialized {
|
||||
@ -238,12 +228,15 @@ func main() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// close listener
|
||||
defer listen.Close()
|
||||
defer func(listen *net.TCPListener) {
|
||||
_ = listen.Close()
|
||||
}(listen)
|
||||
go func() {
|
||||
for {
|
||||
conn, err := listen.AcceptTCP()
|
||||
if err != nil {
|
||||
if opErr, ok := err.(*net.OpError); ok && opErr.Err.Error() == "use of closed network connection" {
|
||||
var opErr *net.OpError
|
||||
if errors.As(err, &opErr) && opErr.Err.Error() == "use of closed network connection" {
|
||||
log.Println("Listener closed, stopping server.")
|
||||
return
|
||||
}
|
||||
@ -349,25 +342,24 @@ func runGameLogic(players map[uint32]*Player, gameMap *GameMap, bases map[uint32
|
||||
//update remote player maps
|
||||
totalRemotePlayerUpdate += profileSection(func() {
|
||||
var wgX sync.WaitGroup
|
||||
var wgX2 sync.WaitGroup
|
||||
playerUpdateMutex.Lock()
|
||||
playersMutex.RLock()
|
||||
for _, player := range players {
|
||||
if player.local {
|
||||
if player.local || !player.initialized {
|
||||
continue
|
||||
}
|
||||
wgX.Add(6)
|
||||
go player.updateRemotePlayerBases(bases, players, &wgX)
|
||||
go player.updateRemotePlayerMap(&wgX)
|
||||
go player.updateRemotePlayerBases(bases, &wgX)
|
||||
go player.updateRemotePlayerBullets(bullets, &wgX)
|
||||
go player.updateRemotePlayerBulletParticles(bulletParticles, &wgX)
|
||||
go player.updateRemotePlayerPlayers(players, &wgX)
|
||||
playerX := player
|
||||
go func() {
|
||||
fail := playerX.sendInfoToPlayer(&wgX)
|
||||
if fail {
|
||||
success := playerX.sendPlayerUpdate(&wgX)
|
||||
if !success {
|
||||
if playerX.connection != nil {
|
||||
(*playerX.connection).Close()
|
||||
_ = (*playerX.connection).Close()
|
||||
}
|
||||
baseMutex.Lock()
|
||||
if bases[playerX.playerID] != nil {
|
||||
@ -382,28 +374,6 @@ func runGameLogic(players map[uint32]*Player, gameMap *GameMap, bases map[uint32
|
||||
}
|
||||
playersMutex.RUnlock()
|
||||
wgX.Wait()
|
||||
|
||||
playersMutex.Lock()
|
||||
for _, player := range players {
|
||||
wgX2.Add(1)
|
||||
playerX := player
|
||||
go func() {
|
||||
fail := playerX.sendUpdatesToPlayer(players, &wgX2)
|
||||
if fail {
|
||||
if playerX.connection != nil {
|
||||
(*playerX.connection).Close()
|
||||
}
|
||||
baseMutex.Lock()
|
||||
if bases[playerX.playerID] != nil {
|
||||
bases[playerX.playerID].delete(bases)
|
||||
}
|
||||
baseMutex.Unlock()
|
||||
delete(players, playerX.playerID)
|
||||
}
|
||||
}()
|
||||
}
|
||||
playersMutex.Unlock()
|
||||
wgX2.Wait()
|
||||
playerUpdateMutex.Unlock()
|
||||
})
|
||||
}
|
||||
@ -443,11 +413,11 @@ func initPlayer(playerIndex uint8, player *Player) {
|
||||
}
|
||||
player.window, player.logicalSurface = setupWindowAndSurface(playerIndex)
|
||||
logicalColor := sdl.MapRGBA(player.logicalSurface.Format, 101, 101, 0, 255)
|
||||
player.logicalSurface.FillRect(nil, logicalColor)
|
||||
_ = player.logicalSurface.FillRect(nil, logicalColor)
|
||||
|
||||
player.playSurface, player.playSurfaceRect, player.playSurfaceTargetRect = setupPlaySurface()
|
||||
playColor := sdl.MapRGBA(player.playSurface.Format, 101, 0, 101, 255)
|
||||
player.playSurface.FillRect(nil, playColor)
|
||||
_ = player.playSurface.FillRect(nil, playColor)
|
||||
|
||||
player.HUDSurface, player.HUDSurfaceRect, player.HUDSurfaceTargetRect = setupHUDSurface()
|
||||
initHud(player.HUDSurface)
|
||||
@ -498,8 +468,8 @@ func doPlayerFrame(playerIndex uint8, player *Player, players map[uint32]*Player
|
||||
player.tick(bullets)
|
||||
player.gameObject.prevBaseRect = player.gameObject.baseRect
|
||||
renderHud(player, player.HUDSurface)
|
||||
player.playSurface.BlitScaled(player.playSurfaceRect, player.logicalSurface, player.playSurfaceTargetRect)
|
||||
player.HUDSurface.BlitScaled(player.HUDSurfaceRect, player.logicalSurface, player.HUDSurfaceTargetRect)
|
||||
_ = player.playSurface.BlitScaled(player.playSurfaceRect, player.logicalSurface, player.playSurfaceTargetRect)
|
||||
_ = player.HUDSurface.BlitScaled(player.HUDSurfaceRect, player.logicalSurface, player.HUDSurfaceTargetRect)
|
||||
})
|
||||
|
||||
// Profile window adjustments
|
||||
|
119
netCode.go
Normal file
119
netCode.go
Normal file
@ -0,0 +1,119 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash/crc32"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// sendPacket sends a packet with the given packetID and data over the provided TCP connection.
|
||||
func sendPacket(packetID uint8, data []byte, conn *net.TCPConn) error {
|
||||
// Create a buffer with the packet ID and data
|
||||
tempBuffer := append([]byte{packetID}, data...)
|
||||
|
||||
// Calculate the checksum of the packet ID + data
|
||||
checksum := crc32.ChecksumIEEE(tempBuffer)
|
||||
|
||||
// Convert the checksum to a 4-byte slice
|
||||
checksumBytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(checksumBytes, checksum)
|
||||
|
||||
// Prepend the checksum to the beginning of the buffer
|
||||
tempBuffer = append(checksumBytes, tempBuffer...)
|
||||
|
||||
// Calculate the total length (checksum + packet ID + data)
|
||||
packetLength := uint32(len(tempBuffer))
|
||||
|
||||
// Prepare the output buffer with the length field
|
||||
outBuffer := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(outBuffer, packetLength)
|
||||
|
||||
// Append the tempBuffer (checksum + packet ID + data) to outBuffer
|
||||
outBuffer = append(outBuffer, tempBuffer...)
|
||||
|
||||
// Send the packet over the connection
|
||||
writeLen, err := conn.Write(outBuffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if writeLen != len(outBuffer) {
|
||||
return errors.New("write length does not match output buffer length")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// receivePacket receives a packet from the provided TCP connection and verifies it.
|
||||
func receivePacket(conn *net.TCPConn) (uint8, uint32, []byte, error) {
|
||||
// Step 1: Read the packet length (4 bytes)
|
||||
lengthBuffer := make([]byte, 4)
|
||||
n, err := conn.Read(lengthBuffer)
|
||||
if err != nil {
|
||||
return 0, 0, nil, err
|
||||
}
|
||||
if n != len(lengthBuffer) {
|
||||
return 0, 0, nil, errors.New("read length does not match read length")
|
||||
}
|
||||
|
||||
packetLength := binary.LittleEndian.Uint32(lengthBuffer)
|
||||
|
||||
// Step 2: Allocate a buffer to hold the rest of the packet (checksum + packetID + data)
|
||||
packetBuffer := make([]byte, packetLength)
|
||||
n, err = conn.Read(packetBuffer)
|
||||
if err != nil {
|
||||
return 0, 0, nil, err
|
||||
}
|
||||
if n != int(packetLength) {
|
||||
return 0, 0, nil, errors.New("read length does not match packet length")
|
||||
}
|
||||
|
||||
// Ensure we've read enough bytes
|
||||
if uint32(len(packetBuffer)) != packetLength {
|
||||
return 0, 0, nil, errors.New("incomplete packet received")
|
||||
}
|
||||
|
||||
// Step 3: Extract the checksum (4 bytes)
|
||||
receivedChecksum := binary.LittleEndian.Uint32(packetBuffer[:4])
|
||||
|
||||
// Step 4: Extract the packet ID (1 byte)
|
||||
packetID := packetBuffer[4]
|
||||
|
||||
// Step 5: Extract the data
|
||||
data := packetBuffer[5:]
|
||||
|
||||
// Step 6: Verify the checksum
|
||||
calculatedChecksum := crc32.ChecksumIEEE(append([]byte{packetID}, data...))
|
||||
|
||||
if receivedChecksum != calculatedChecksum {
|
||||
return 0, 0, nil, errors.New("checksum mismatch")
|
||||
}
|
||||
|
||||
// Return the packet ID and data
|
||||
return packetID, uint32(len(data)), data, nil
|
||||
}
|
||||
|
||||
func boolToByte(b bool) byte {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func byteToBool(b byte) bool {
|
||||
return b == 1
|
||||
}
|
||||
|
||||
func getCurrentVersion() []uint8 {
|
||||
versionArrayString := strings.Split(GameVersion, ".")
|
||||
if len(versionArrayString) < 3 {
|
||||
return []uint8{}
|
||||
}
|
||||
var versionArray []uint8
|
||||
for i := 0; i < len(versionArrayString); i++ {
|
||||
intVersion, _ := strconv.Atoi(versionArrayString[i])
|
||||
versionArray = append(versionArray, uint8(intVersion))
|
||||
}
|
||||
return versionArray
|
||||
}
|
@ -379,7 +379,9 @@ func handleInput(keyboard []uint8, bullets map[uint32]*Bullet, player *Player, g
|
||||
// Handle movement after the loop
|
||||
if moveUp || moveDown || moveLeft || moveRight {
|
||||
if player.tryMove(gameMap, shoot, players) {
|
||||
player.energy -= serverConfig.MovementCost
|
||||
if player.energy > serverConfig.MovementCost {
|
||||
player.energy -= serverConfig.MovementCost
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
316
playerutil.go
Normal file
316
playerutil.go
Normal file
@ -0,0 +1,316 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
"math/rand"
|
||||
"net"
|
||||
)
|
||||
|
||||
func getUnusedColor(colors []PlayerColors, players map[uint32]*Player) uint32 {
|
||||
for i, c := range colors {
|
||||
foundPlayerWithColor := false
|
||||
var colorCompare []uint8
|
||||
clrR, clrG, clrB, clrA := c.tracks.RGBA()
|
||||
colorCompare = append(colorCompare, uint8(clrR), uint8(clrG), uint8(clrB), uint8(clrA))
|
||||
clrR, clrG, clrB, clrA = c.body.RGBA()
|
||||
colorCompare = append(colorCompare, uint8(clrR), uint8(clrG), uint8(clrB), uint8(clrA))
|
||||
clrR, clrG, clrB, clrA = c.cannon.RGBA()
|
||||
colorCompare = append(colorCompare, uint8(clrR), uint8(clrG), uint8(clrB), uint8(clrA))
|
||||
playersMutex.RLock()
|
||||
for _, player := range players {
|
||||
var playerColorsCompare []uint8
|
||||
plrR, plrG, plrB, plrA := player.playerColors.tracks.RGBA()
|
||||
playerColorsCompare = append(playerColorsCompare, uint8(plrR), uint8(plrG), uint8(plrB), uint8(plrA))
|
||||
plrR, plrG, plrB, plrA = player.playerColors.body.RGBA()
|
||||
playerColorsCompare = append(playerColorsCompare, uint8(plrR), uint8(plrG), uint8(plrB), uint8(plrA))
|
||||
plrR, plrG, plrB, plrA = player.playerColors.cannon.RGBA()
|
||||
playerColorsCompare = append(playerColorsCompare, uint8(plrR), uint8(plrG), uint8(plrB), uint8(plrA))
|
||||
foundPlayerWithColor = false
|
||||
if len(playerColorsCompare) == len(colorCompare) {
|
||||
for i, c := range colorCompare {
|
||||
if playerColorsCompare[i] == c {
|
||||
foundPlayerWithColor = true
|
||||
} else {
|
||||
foundPlayerWithColor = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if foundPlayerWithColor {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
playersMutex.RUnlock()
|
||||
if !foundPlayerWithColor {
|
||||
return uint32(i)
|
||||
}
|
||||
}
|
||||
return uint32(len(colors) - 1)
|
||||
}
|
||||
|
||||
func createPlayers(amount uint8, playerColors []PlayerColors, keyMaps []KeyMap, joyMaps []JoyMap, gameMap *GameMap, players map[uint32]*Player, bases map[uint32]*Base) {
|
||||
joyStickCount := sdl.NumJoysticks()
|
||||
if amount > uint8(len(keyMaps)+len(joyMaps)) || amount > uint8(len(keyMaps)+joyStickCount) {
|
||||
panic("Too many players, not enough inputs")
|
||||
}
|
||||
if amount >= uint8(len(playerColors)) {
|
||||
panic("Too many players, not enough colors")
|
||||
}
|
||||
addedKeyboardPlayers := 0
|
||||
for i := uint8(0); i < amount; i++ {
|
||||
var keyMap KeyMap
|
||||
var joyMap JoyMap
|
||||
var joyStick *sdl.Joystick
|
||||
|
||||
if (config.DoAllKeymapsPlayers && i <= uint8(len(keyMaps))) || (config.DoKeymapPlayer && i == 0) || (uint8(joyStickCount) <= i) {
|
||||
keyMap = keyMaps[(addedKeyboardPlayers+int(config.KeyBindOffset))%(len(keyMaps)-1)]
|
||||
addedKeyboardPlayers++
|
||||
} else {
|
||||
joyStickIndex := i - uint8(addedKeyboardPlayers)
|
||||
joyMap = joyMaps[joyStickIndex]
|
||||
joyStick = sdl.JoystickOpen(int(joyStickIndex))
|
||||
}
|
||||
unusedColor := getUnusedColor(playerColors, players)
|
||||
createPlayer(
|
||||
true,
|
||||
playerColors[unusedColor],
|
||||
unusedColor,
|
||||
nil,
|
||||
keyMap,
|
||||
joyMap,
|
||||
joyStick,
|
||||
gameMap,
|
||||
players,
|
||||
bases,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func closeThings(players map[uint32]*Player) {
|
||||
playersMutex.Lock()
|
||||
for _, player := range players {
|
||||
if player.joyStick != nil {
|
||||
player.joyStick.Close()
|
||||
}
|
||||
if player.window != nil {
|
||||
_ = player.window.Destroy()
|
||||
}
|
||||
if player.logicalSurface != nil {
|
||||
player.logicalSurface.Free()
|
||||
}
|
||||
if player.playSurface != nil {
|
||||
player.playSurface.Free()
|
||||
}
|
||||
if player.HUDSurface != nil {
|
||||
player.HUDSurface.Free()
|
||||
}
|
||||
}
|
||||
playersMutex.Unlock()
|
||||
}
|
||||
|
||||
func createPlayer(local bool, thisPlayerColors PlayerColors, thisPlayerColorIndex uint32, conn *net.TCPConn, keyMap KeyMap, joyMap JoyMap, joyStick *sdl.Joystick, gameMap *GameMap, players map[uint32]*Player, bases map[uint32]*Base) uint32 {
|
||||
coordsAreValid := false
|
||||
var posX, posY uint32
|
||||
maxTries := 1000
|
||||
baseSize := uint32(36) // Since the base is 36x36
|
||||
minDistance := uint32(200) // Minimum distance between bases
|
||||
maxDistance := uint32(500) // Maximum distance between bases
|
||||
|
||||
for !coordsAreValid && maxTries >= 0 {
|
||||
maxTries--
|
||||
posX = uint32(16 + rand.Intn(int(gameMap.width-baseSize-16)))
|
||||
posY = uint32(16 + rand.Intn(int(gameMap.height-baseSize-16)))
|
||||
|
||||
coordsAreValid = true
|
||||
baseMutex.RLock()
|
||||
for _, base := range bases {
|
||||
basePosX := uint32(base.gameObject.baseRect.X)
|
||||
basePosY := uint32(base.gameObject.baseRect.Y)
|
||||
|
||||
// Calculate the distance between the edges of the bases
|
||||
distanceX := max(0, max(basePosX-posX-baseSize, posX-basePosX-baseSize))
|
||||
distanceY := max(0, max(basePosY-posY-baseSize, posY-basePosY-baseSize))
|
||||
|
||||
distanceSquared := distanceX*distanceX + distanceY*distanceY
|
||||
|
||||
if distanceSquared < minDistance*minDistance && distanceSquared > maxDistance*maxDistance {
|
||||
coordsAreValid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
baseMutex.RUnlock()
|
||||
|
||||
// Edge clamping to ensure the base is within the map boundaries
|
||||
if posX < 16 || posX > gameMap.width-baseSize-16 || posY < 16 || posY > gameMap.height-baseSize-16 {
|
||||
coordsAreValid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if maxTries < 0 {
|
||||
panic("Could not place all players, increase map size")
|
||||
}
|
||||
|
||||
gameObject := &GameObject{}
|
||||
|
||||
gameObject.baseRect = &sdl.Rect{
|
||||
X: int32(posX),
|
||||
Y: int32(posY),
|
||||
W: 7,
|
||||
H: 7,
|
||||
}
|
||||
|
||||
gameObject.addColor(thisPlayerColors.tracks)
|
||||
gameObject.addColor(thisPlayerColors.body)
|
||||
gameObject.addColor(thisPlayerColors.cannon)
|
||||
|
||||
gameObject.orientation = 0 // Up
|
||||
gameObject.addColoredRect(0, 1, 1, 6, 0)
|
||||
gameObject.addColoredRect(4, 1, 1, 6, 0)
|
||||
gameObject.addColoredRect(1, 2, 3, 4, 1)
|
||||
gameObject.addColoredRect(2, 0, 1, 4, 2)
|
||||
|
||||
gameObject.orientation = 1 // Right
|
||||
gameObject.addColoredRect(0, 0, 6, 1, 0)
|
||||
gameObject.addColoredRect(0, 4, 6, 1, 0)
|
||||
gameObject.addColoredRect(1, 1, 4, 3, 1)
|
||||
gameObject.addColoredRect(3, 2, 4, 1, 2)
|
||||
|
||||
gameObject.orientation = 2 // Down
|
||||
gameObject.addColoredRect(0, 0, 1, 6, 0)
|
||||
gameObject.addColoredRect(4, 0, 1, 6, 0)
|
||||
gameObject.addColoredRect(1, 1, 3, 4, 1)
|
||||
gameObject.addColoredRect(2, 3, 1, 4, 2)
|
||||
|
||||
gameObject.orientation = 3 // Left
|
||||
gameObject.addColoredRect(1, 0, 6, 1, 0)
|
||||
gameObject.addColoredRect(1, 4, 6, 1, 0)
|
||||
gameObject.addColoredRect(2, 1, 4, 3, 1)
|
||||
gameObject.addColoredRect(0, 2, 4, 1, 2)
|
||||
|
||||
gameObject.orientation = 4 // Up-Right
|
||||
gameObject.addColoredRect(3, 0, 1, 1, 0)
|
||||
gameObject.addColoredRect(2, 1, 1, 1, 0)
|
||||
gameObject.addColoredRect(1, 2, 1, 1, 0)
|
||||
gameObject.addColoredRect(0, 3, 1, 1, 0)
|
||||
gameObject.addColoredRect(6, 3, 1, 1, 0)
|
||||
gameObject.addColoredRect(5, 4, 1, 1, 0)
|
||||
gameObject.addColoredRect(4, 5, 1, 1, 0)
|
||||
gameObject.addColoredRect(3, 6, 1, 1, 0)
|
||||
|
||||
gameObject.addColoredRect(3, 1, 1, 1, 1)
|
||||
gameObject.addColoredRect(2, 2, 2, 1, 1)
|
||||
gameObject.addColoredRect(1, 3, 2, 1, 1)
|
||||
|
||||
gameObject.addColoredRect(4, 3, 2, 1, 1)
|
||||
gameObject.addColoredRect(2, 4, 3, 1, 1)
|
||||
gameObject.addColoredRect(3, 5, 1, 1, 1)
|
||||
|
||||
gameObject.addColoredRect(5, 1, 1, 1, 2)
|
||||
gameObject.addColoredRect(4, 2, 1, 1, 2)
|
||||
gameObject.addColoredRect(3, 3, 1, 1, 2)
|
||||
|
||||
// Up-Left orientation (Y-axis reflection)
|
||||
gameObject.orientation = 5 // Up-Left
|
||||
gameObject.addColoredRect(3, 0, 1, 1, 0)
|
||||
gameObject.addColoredRect(4, 1, 1, 1, 0)
|
||||
gameObject.addColoredRect(5, 2, 1, 1, 0)
|
||||
gameObject.addColoredRect(6, 3, 1, 1, 0)
|
||||
gameObject.addColoredRect(0, 3, 1, 1, 0)
|
||||
gameObject.addColoredRect(1, 4, 1, 1, 0)
|
||||
gameObject.addColoredRect(2, 5, 1, 1, 0)
|
||||
gameObject.addColoredRect(3, 6, 1, 1, 0)
|
||||
|
||||
gameObject.addColoredRect(3, 1, 1, 1, 1)
|
||||
gameObject.addColoredRect(3, 2, 2, 1, 1)
|
||||
gameObject.addColoredRect(4, 3, 2, 1, 1)
|
||||
|
||||
gameObject.addColoredRect(1, 3, 2, 1, 1)
|
||||
gameObject.addColoredRect(2, 4, 3, 1, 1)
|
||||
gameObject.addColoredRect(3, 5, 1, 1, 1)
|
||||
|
||||
gameObject.addColoredRect(1, 1, 1, 1, 2)
|
||||
gameObject.addColoredRect(2, 2, 1, 1, 2)
|
||||
gameObject.addColoredRect(3, 3, 1, 1, 2)
|
||||
|
||||
// Down-Right orientation (X-axis reflection)
|
||||
gameObject.orientation = 6 // Down-Right
|
||||
gameObject.addColoredRect(3, 6, 1, 1, 0)
|
||||
gameObject.addColoredRect(2, 5, 1, 1, 0)
|
||||
gameObject.addColoredRect(1, 4, 1, 1, 0)
|
||||
gameObject.addColoredRect(0, 3, 1, 1, 0)
|
||||
gameObject.addColoredRect(6, 3, 1, 1, 0)
|
||||
gameObject.addColoredRect(5, 2, 1, 1, 0)
|
||||
gameObject.addColoredRect(4, 1, 1, 1, 0)
|
||||
gameObject.addColoredRect(3, 0, 1, 1, 0)
|
||||
|
||||
gameObject.addColoredRect(3, 5, 1, 1, 1)
|
||||
gameObject.addColoredRect(2, 4, 2, 1, 1)
|
||||
gameObject.addColoredRect(1, 3, 2, 1, 1)
|
||||
|
||||
gameObject.addColoredRect(4, 3, 2, 1, 1)
|
||||
gameObject.addColoredRect(2, 2, 3, 1, 1)
|
||||
gameObject.addColoredRect(3, 1, 1, 1, 1)
|
||||
|
||||
gameObject.addColoredRect(5, 5, 1, 1, 2)
|
||||
gameObject.addColoredRect(4, 4, 1, 1, 2)
|
||||
gameObject.addColoredRect(3, 3, 1, 1, 2)
|
||||
|
||||
// Down-Left orientation (XY reflection)
|
||||
gameObject.orientation = 7 // Down-Left
|
||||
gameObject.addColoredRect(3, 6, 1, 1, 0)
|
||||
gameObject.addColoredRect(4, 5, 1, 1, 0)
|
||||
gameObject.addColoredRect(5, 4, 1, 1, 0)
|
||||
gameObject.addColoredRect(6, 3, 1, 1, 0)
|
||||
gameObject.addColoredRect(0, 3, 1, 1, 0)
|
||||
gameObject.addColoredRect(1, 2, 1, 1, 0)
|
||||
gameObject.addColoredRect(2, 1, 1, 1, 0)
|
||||
gameObject.addColoredRect(3, 0, 1, 1, 0)
|
||||
|
||||
gameObject.addColoredRect(3, 1, 1, 1, 1)
|
||||
gameObject.addColoredRect(2, 2, 3, 1, 1)
|
||||
gameObject.addColoredRect(1, 3, 2, 1, 1)
|
||||
|
||||
gameObject.addColoredRect(4, 3, 2, 1, 1)
|
||||
gameObject.addColoredRect(3, 4, 2, 1, 1)
|
||||
gameObject.addColoredRect(3, 5, 1, 1, 1)
|
||||
|
||||
gameObject.addColoredRect(1, 5, 1, 1, 2)
|
||||
gameObject.addColoredRect(2, 4, 1, 1, 2)
|
||||
gameObject.addColoredRect(3, 3, 1, 1, 2)
|
||||
|
||||
gameObject.orientation = 0
|
||||
gameObject.adjustBaseRect()
|
||||
|
||||
if !local && (keyMap.exit != keyMap.shoot || joyMap.exitButton != joyMap.shootButton) {
|
||||
panic("Input assigned to remote player")
|
||||
}
|
||||
|
||||
knownGameMap := GameMap{width: gameMap.width, height: gameMap.height, tiles: make([][]uint8, serverConfig.MapWidth)}
|
||||
for i := uint32(0); i < serverConfig.MapWidth; i++ {
|
||||
knownGameMap.tiles[i] = make([]uint8, serverConfig.MapHeight)
|
||||
}
|
||||
|
||||
playersMutex.Lock()
|
||||
players[lastPlayerID] = &Player{
|
||||
playerColors: thisPlayerColors,
|
||||
playerColorID: thisPlayerColorIndex,
|
||||
keyMap: keyMap,
|
||||
joyMap: joyMap,
|
||||
joyStick: joyStick,
|
||||
shields: serverConfig.MaxShields,
|
||||
energy: serverConfig.MaxEnergy,
|
||||
gameObject: gameObject,
|
||||
local: local,
|
||||
connection: conn,
|
||||
playerID: lastPlayerID,
|
||||
knownBulletParticles: make(map[uint32]*BulletParticle),
|
||||
knownBases: make(map[uint32]*Base),
|
||||
knownPlayers: make(map[uint32]*Player),
|
||||
knownBullets: make(map[uint32]*Bullet),
|
||||
knownGameMap: &knownGameMap,
|
||||
}
|
||||
playersMutex.Unlock()
|
||||
lastPlayerID++
|
||||
return lastPlayerID - 1
|
||||
}
|
236
serverboundpacket.go
Normal file
236
serverboundpacket.go
Normal file
@ -0,0 +1,236 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
135
tuneller.proto
135
tuneller.proto
@ -1,135 +0,0 @@
|
||||
syntax = "proto3";
|
||||
package goingtunneling;
|
||||
option go_package = "./proto";
|
||||
|
||||
message Position {
|
||||
int32 posX = 1;
|
||||
int32 posY = 2;
|
||||
}
|
||||
|
||||
message Player {
|
||||
uint32 playerID = 1;
|
||||
PlayerLocation location = 2;
|
||||
}
|
||||
|
||||
message PlayerLocation {
|
||||
Position position = 1;
|
||||
uint32 orientation = 2;
|
||||
}
|
||||
|
||||
message Bullet {
|
||||
Position position = 1;
|
||||
uint32 direction = 2;
|
||||
Color color = 3;
|
||||
uint32 id = 4;
|
||||
bool super = 5;
|
||||
uint32 ownerID = 6;
|
||||
}
|
||||
|
||||
message Color {
|
||||
uint32 red = 1;
|
||||
uint32 green = 2;
|
||||
uint32 blue = 3;
|
||||
uint32 alpha = 4;
|
||||
}
|
||||
|
||||
message BulletParticle {
|
||||
Position position = 1;
|
||||
uint32 expirationTimer = 2;
|
||||
Color color = 3;
|
||||
uint32 id = 4;
|
||||
}
|
||||
|
||||
message ServerInfo {
|
||||
uint32 maxEnergy = 1;
|
||||
uint32 maxAmmunition = 2;
|
||||
uint32 maxShields = 3;
|
||||
uint32 mapWidth = 4;
|
||||
uint32 mapHeight = 5;
|
||||
uint32 normalShotCost = 6;
|
||||
uint32 superShotCost = 7;
|
||||
uint32 reloadCost = 8;
|
||||
uint32 movementCost = 9;
|
||||
uint32 diggingCost = 10;
|
||||
uint32 shootDiggingBonus = 11;
|
||||
uint32 shootCooldown = 12;
|
||||
uint32 rechargeCooldown = 13;
|
||||
uint32 rechargeOpponentCooldown = 14;
|
||||
uint32 repairCooldown = 15;
|
||||
uint32 diggingCooldown = 16;
|
||||
uint32 movementCooldown = 17;
|
||||
uint32 movementCooldownNoEnergy = 18;
|
||||
uint32 diggingCooldownNoEnergy = 19;
|
||||
uint32 reloadCooldown = 20;
|
||||
uint32 blastRadius = 21;
|
||||
uint32 playerID = 22;
|
||||
uint32 playerColorID = 23;
|
||||
uint32 reloadWait = 24;
|
||||
}
|
||||
|
||||
message PlayerUpdate {
|
||||
uint32 energy = 1;
|
||||
uint32 ammo = 2;
|
||||
uint32 shields = 3;
|
||||
}
|
||||
|
||||
message BaseLocation {
|
||||
Position position = 1;
|
||||
Player owner = 2;
|
||||
Color color = 3;
|
||||
}
|
||||
|
||||
message TileUpdate {
|
||||
Position position = 1;
|
||||
uint32 kind = 2;
|
||||
}
|
||||
|
||||
message WorldUpdate {
|
||||
repeated Player players = 1;
|
||||
repeated BaseLocation base = 2;
|
||||
repeated Bullet bullets = 3;
|
||||
repeated BulletParticle bulletParticles = 4;
|
||||
repeated TileUpdate tileUpdate = 5;
|
||||
}
|
||||
|
||||
message ClientBound {
|
||||
oneof clientBoundMessage {
|
||||
ServerInfo serverInfo = 1;
|
||||
PlayerUpdate playerUpdate = 2;
|
||||
PlayerLocation playerLocationUpdate = 3;
|
||||
WorldUpdate worldUpdate = 4;
|
||||
PlayerStartResponse playerStartResponse = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message DigBlock {
|
||||
Position position = 1;
|
||||
bool isShooting = 2;
|
||||
}
|
||||
|
||||
message Shoot {
|
||||
bool super = 1;
|
||||
}
|
||||
|
||||
message PlayerAction {
|
||||
oneof playerAction {
|
||||
DigBlock digBlock = 1;
|
||||
Shoot shoot = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message PlayerStartRequest {
|
||||
string version = 1;
|
||||
}
|
||||
|
||||
message PlayerStartResponse {
|
||||
string version = 1;
|
||||
}
|
||||
|
||||
message ServerBound {
|
||||
oneof serverBoundMessage {
|
||||
PlayerLocation playerPosition = 1;
|
||||
PlayerAction playerAction = 2;
|
||||
PlayerStartRequest playerStartRequest = 3;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user