components
API
components
packageAPI reference for the components
package.
Imports
(5)
S
struct
multiplayerState
docs/host/components/multiplayer_host.go:37-41
type multiplayerState struct
Fields
| Name | Type | Description |
|---|---|---|
| Players | map[string]multiplayerPlayer | json:"players" |
| Bullets | []multiplayerBullet | json:"bullets" |
| Winner | string | json:"winner" |
S
struct
multiplayerPlayer
docs/host/components/multiplayer_host.go:43-55
type multiplayerPlayer struct
Fields
| Name | Type | Description |
|---|---|---|
| ID | string | json:"id" |
| X | float64 | json:"x" |
| Y | float64 | json:"y" |
| VX | float64 | json:"vx" |
| VY | float64 | json:"vy" |
| AimX | float64 | json:"aimX" |
| AimY | float64 | json:"aimY" |
| Color | string | json:"color" |
| Lives | int | json:"lives" |
| Alive | bool | json:"alive" |
| Cooldown | float64 | json:"-" |
S
struct
multiplayerBullet
docs/host/components/multiplayer_host.go:57-64
type multiplayerBullet struct
Fields
| Name | Type | Description |
|---|---|---|
| X | float64 | json:"x" |
| Y | float64 | json:"y" |
| VX | float64 | json:"vx" |
| VY | float64 | json:"vy" |
| Owner | string | json:"owner" |
| Life | float64 | json:"-" |
F
function
RegisterMultiplayerHost
RegisterMultiplayerHost exposes the multiplayer arena netcode server.
docs/host/components/multiplayer_host.go:67-84
func RegisterMultiplayerHost()
{
initial := multiplayerState{Players: make(map[string]multiplayerPlayer)}
srv := netcode.NewServer(multiplayerChannel, initial, applyMultiplayerCommand)
host.Register(srv.HostComponent())
ticker := time.NewTicker(multiplayerTick)
go func() {
defer ticker.Stop()
var tick int64
for range ticker.C {
tick += int64(multiplayerTick / time.Millisecond)
srv.Update(func(state *multiplayerState) {
stepMultiplayer(state, multiplayerTick.Seconds())
})
srv.Broadcast(tick)
}
}()
}
F
function
applyMultiplayerCommand
Parameters
state
cmd
any
docs/host/components/multiplayer_host.go:86-156
func applyMultiplayerCommand(state *multiplayerState, cmd any)
{
payload, ok := cmd.(map[string]any)
if !ok {
return
}
session, _ := payload["session"].(string)
if session == "" {
return
}
if state.Players == nil {
state.Players = make(map[string]multiplayerPlayer)
}
action, _ := payload["type"].(string)
switch action {
case "join":
ensurePlayer(state, session)
state.Winner = ""
return
}
player := ensurePlayer(state, session)
if !player.Alive {
state.Players[session] = player
return
}
dx := floatFrom(payload["dx"])
dy := floatFrom(payload["dy"])
mag := math.Hypot(dx, dy)
if mag > 1 {
dx /= mag
dy /= mag
mag = 1
}
player.VX = dx * playerSpeed
player.VY = dy * playerSpeed
aimX := floatFrom(payload["aimX"])
aimY := floatFrom(payload["aimY"])
if aimLen := math.Hypot(aimX, aimY); aimLen > 0 {
player.AimX = aimX / aimLen
player.AimY = aimY / aimLen
} else if mag > 0 {
norm := math.Hypot(dx, dy)
if norm > 0 {
player.AimX = dx / norm
player.AimY = dy / norm
}
}
shoot := boolFrom(payload["shoot"])
if shoot && player.Cooldown <= 0 && player.Alive {
ax, ay := player.AimX, player.AimY
if math.Hypot(ax, ay) == 0 {
ax, ay = 0, -1
player.AimX, player.AimY = ax, ay
}
bullet := multiplayerBullet{
X: player.X + ax*(playerRadius+bulletRadius),
Y: player.Y + ay*(playerRadius+bulletRadius),
VX: ax * bulletSpeed,
VY: ay * bulletSpeed,
Owner: session,
}
state.Bullets = append(state.Bullets, bullet)
player.Cooldown = shootCooldownSeconds
}
state.Players[session] = player
}
F
function
stepMultiplayer
Parameters
state
dt
float64
docs/host/components/multiplayer_host.go:158-241
func stepMultiplayer(state *multiplayerState, dt float64)
{
if state.Players == nil {
state.Players = make(map[string]multiplayerPlayer)
}
for id, player := range state.Players {
if player.Cooldown > 0 {
player.Cooldown -= dt
if player.Cooldown < 0 {
player.Cooldown = 0
}
}
if player.Alive {
player.X += player.VX * dt
player.Y += player.VY * dt
if player.X < playerRadius {
player.X = playerRadius
}
if player.X > arenaWidth-playerRadius {
player.X = arenaWidth - playerRadius
}
if player.Y < playerRadius {
player.Y = playerRadius
}
if player.Y > arenaHeight-playerRadius {
player.Y = arenaHeight - playerRadius
}
}
state.Players[id] = player
}
newBullets := make([]multiplayerBullet, 0, len(state.Bullets))
for _, bullet := range state.Bullets {
bullet.X += bullet.VX * dt
bullet.Y += bullet.VY * dt
bullet.Life += dt
if bullet.Life > bulletLifetime {
continue
}
if bullet.X < 0 || bullet.X > arenaWidth || bullet.Y < 0 || bullet.Y > arenaHeight {
continue
}
hit := false
for id, player := range state.Players {
if !player.Alive || id == bullet.Owner {
continue
}
if overlaps(bullet.X, bullet.Y, player.X, player.Y, playerRadius+bulletRadius) {
player.Lives--
if player.Lives < 0 {
player.Lives = 0
}
if player.Lives <= 0 {
player.Alive = false
player.VX, player.VY = 0, 0
}
state.Players[id] = player
hit = true
break
}
}
if !hit {
newBullets = append(newBullets, bullet)
}
}
state.Bullets = newBullets
aliveCount := 0
lastAlive := ""
for id, player := range state.Players {
if player.Alive {
aliveCount++
lastAlive = id
}
}
if aliveCount == 1 {
state.Winner = lastAlive
} else if aliveCount == 0 {
state.Winner = ""
} else {
state.Winner = ""
}
}
F
function
ensurePlayer
Parameters
state
session
string
Returns
docs/host/components/multiplayer_host.go:243-261
func ensurePlayer(state *multiplayerState, session string) multiplayerPlayer
{
if player, ok := state.Players[session]; ok {
return player
}
spawnX := playerRadius + rand.Float64()*(arenaWidth-2*playerRadius)
spawnY := playerRadius + rand.Float64()*(arenaHeight-2*playerRadius)
player := multiplayerPlayer{
ID: session,
X: spawnX,
Y: spawnY,
AimX: 0,
AimY: -1,
Color: pickColor(state),
Lives: maxLives,
Alive: true,
}
state.Players[session] = player
return player
}
F
function
pickColor
Parameters
state
Returns
string
docs/host/components/multiplayer_host.go:263-274
func pickColor(state *multiplayerState) string
{
used := make(map[string]bool, len(state.Players))
for _, player := range state.Players {
used[player.Color] = true
}
for _, color := range colorPalette {
if !used[color] {
return color
}
}
return colorPalette[rand.Intn(len(colorPalette))]
}
F
function
overlaps
Parameters
ax
float64
ay
float64
bx
float64
by
float64
radius
float64
Returns
bool
docs/host/components/multiplayer_host.go:276-280
func overlaps(ax, ay, bx, by, radius float64) bool
{
dx := ax - bx
dy := ay - by
return dx*dx+dy*dy <= radius*radius
}
F
function
floatFrom
Parameters
v
any
Returns
float64
docs/host/components/multiplayer_host.go:282-294
func floatFrom(v any) float64
{
switch val := v.(type) {
case float64:
return val
case float32:
return float64(val)
case int:
return float64(val)
case int64:
return float64(val)
}
return 0
}
F
function
boolFrom
Parameters
v
any
Returns
bool
docs/host/components/multiplayer_host.go:296-306
func boolFrom(v any) bool
{
switch val := v.(type) {
case bool:
return val
case float64:
return val != 0
case int:
return val != 0
}
return false
}
F
function
init
docs/host/components/multiplayer_host.go:308-310
func init()
{
rand.Seed(time.Now().UnixNano())
}
S
struct
ncState
docs/host/components/netcode_host.go:10-12
type ncState struct
Fields
| Name | Type | Description |
|---|---|---|
| X | float64 | json:"x" |
F
function
RegisterNetcodeHost
RegisterNetcodeHost sets up a netcode server broadcasting position updates.
docs/host/components/netcode_host.go:15-32
func RegisterNetcodeHost()
{
srv := netcode.NewServer("NetcodeHost", ncState{}, func(s *ncState, cmd any) {
if m, ok := cmd.(map[string]any); ok {
if dx, ok := m["dx"].(float64); ok {
s.X += dx
}
}
})
host.Register(srv.HostComponent())
go func() {
ticker := time.NewTicker(50 * time.Millisecond)
var tick int64
for range ticker.C {
tick += 50
srv.Broadcast(tick)
}
}()
}
F
function
RegisterSSCHost
docs/host/components/ssc_host.go:9-21
func RegisterSSCHost()
{
var counter int
host.Register(host.NewHostComponent("SSCHost", func(_ map[string]any) any {
return map[string]any{"value": counter}
}))
go func() {
ticker := time.NewTicker(5 * time.Second)
for range ticker.C {
counter++
host.Broadcast("SSCHost", map[string]any{"value": counter})
}
}()
}