Archived
1
0
Fork 0

Add server-side linear bullet integrator

This commit is contained in:
Sebastian Benjamin 2025-02-03 15:33:45 -08:00
parent ef9cfcfab5
commit 8654b46338
3 changed files with 102 additions and 6 deletions

33
server/bullets.go Normal file
View file

@ -0,0 +1,33 @@
package main
const (
BULLET_LINEAR = iota
)
const (
STAGE_WIDTH float64 = 90.0
STAGE_HEIGHT float64 = 160.0
BULLET_KILL_BUFFER_WIDTH float64 = 16.0
)
type Bullet struct {
Class int
SpawnTime int64
SpawnX float64
SpawnY float64
Parameters []float64
}
func (b Bullet) GetCurrentPos(tick int64) (float64, float64) {
switch b.Class {
case BULLET_LINEAR:
return b.SpawnX + (b.Parameters[0] * float64(tick-b.SpawnTime)), b.SpawnY + (b.Parameters[1] * float64(tick-b.SpawnTime))
default:
return b.SpawnX, b.SpawnY
}
}
func (b Bullet) BeyondKillBoundary(tick int64) bool {
x, y := b.GetCurrentPos(tick)
return (x < 0.0-BULLET_KILL_BUFFER_WIDTH) || (x > STAGE_WIDTH+BULLET_KILL_BUFFER_WIDTH) || (y < 0.0-BULLET_KILL_BUFFER_WIDTH) || (y > STAGE_HEIGHT+BULLET_KILL_BUFFER_WIDTH)
}

1
server/collision.go Normal file
View file

@ -0,0 +1 @@
package main

View file

@ -3,26 +3,51 @@ package main
import (
"context"
"database/sql"
"encoding/json"
"math/rand"
"slices"
"github.com/heroiclabs/nakama-common/runtime"
)
const (
MATCH_LOADING = iota
MATCH_START
SPAWN_BULLET
MATCH_END
)
// Interface for registering match handlers
type BattleRoyaleMatch struct{}
type PlayerStageState struct {
xPos float64
yPos float64
bullets []Bullet
}
type PresenceState struct { // present time! hahahahahahahah!
presence runtime.Presence
stageState PlayerStageState
}
// In-memory game state
type BattleRoyaleMatchState struct {
presences map[string]runtime.Presence
emptyTicks int
tickRate int
currentMatchPhase int
presences map[string]*PresenceState
emptyTicks int
}
// Run on match start, initializes game state and sets tick rate
func (m *BattleRoyaleMatch) MatchInit(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, params map[string]interface{}) (interface{}, int, string) {
tickRate := 20 // MatchLoop invocations per second
state := &BattleRoyaleMatchState{
presences: map[string]runtime.Presence{},
emptyTicks: 0,
tickRate: tickRate,
presences: map[string]*PresenceState{},
emptyTicks: 0,
currentMatchPhase: MATCH_LOADING,
}
tickRate := 1 // MatchLoop invocations per second
label := ""
return state, tickRate, label
}
@ -49,7 +74,14 @@ func (m *BattleRoyaleMatch) MatchJoin(ctx context.Context, logger runtime.Logger
}
for i := 0; i < len(presences); i++ {
lobbyState.presences[presences[i].GetSessionId()] = presences[i]
lobbyState.presences[presences[i].GetSessionId()] = &PresenceState{
presence: presences[i],
stageState: PlayerStageState{
xPos: STAGE_WIDTH * 0.5,
yPos: STAGE_HEIGHT - STAGE_HEIGHT*0.1,
bullets: []Bullet{},
},
}
}
return lobbyState
@ -111,6 +143,36 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger
return nil
}
// Test bullet spawning
if tick%20 == 0 {
for _, v := range lobbyState.presences {
vel := rand.Float64()*(STAGE_WIDTH/float64(lobbyState.tickRate)) + 1.0
bullet := Bullet{
BULLET_LINEAR,
tick,
STAGE_WIDTH * rand.Float64(),
STAGE_HEIGHT * rand.Float64(),
[]float64{vel, vel},
}
data, err := json.Marshal(bullet)
if err != nil {
logger.Error("Error marshalling bullet data", err)
} else {
v.stageState.bullets = append(v.stageState.bullets, bullet)
reliable := true
dispatcher.BroadcastMessage(SPAWN_BULLET, data, nil, nil, reliable)
}
}
}
// Bullet cleanup
for _, v := range lobbyState.presences {
v.stageState.bullets = slices.DeleteFunc(v.stageState.bullets, func(b Bullet) bool {
return b.BeyondKillBoundary(tick)
})
}
return lobbyState
}