diff --git a/client/danmaku!/Board.tscn b/client/danmaku!/Board.tscn index 5541662..7e92fd5 100644 --- a/client/danmaku!/Board.tscn +++ b/client/danmaku!/Board.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=5 format=3 uid="uid://dsiowq0rnacln"] -[ext_resource type="PackedScene" uid="uid://cd3tqt7hr5pqs" path="res://danmaku!/Player.tscn" id="1_22cjd"] +[ext_resource type="PackedScene" uid="uid://qvo806pvgbdn" path="res://danmaku!/Player.tscn" id="1_22cjd"] [ext_resource type="Script" uid="uid://ggkxv1cb1bjk" path="res://danmaku!/scaling.gd" id="1_o1mqp"] [ext_resource type="Script" uid="uid://cd67rrch5h4t7" path="res://danmaku!/network_manager.gd" id="2_b2dol"] [ext_resource type="Script" uid="uid://ddc5iqgtyv2ns" path="res://danmaku!/BulletManager.gd" id="4_ubrrh"] diff --git a/client/danmaku!/player.tscn b/client/danmaku!/player.tscn index 8b362ef..df6b685 100644 --- a/client/danmaku!/player.tscn +++ b/client/danmaku!/player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=7 format=3 uid="uid://cd3tqt7hr5pqs"] +[gd_scene load_steps=7 format=3 uid="uid://qvo806pvgbdn"] [ext_resource type="Script" uid="uid://bhwiun72wpk6e" path="res://danmaku!/player.gd" id="1_r7xhp"] [ext_resource type="Texture2D" uid="uid://bs3fntlmlqpt2" path="res://icon.svg" id="2_04s0l"] diff --git a/server/ffi.go b/server/ffi.go new file mode 100644 index 0000000..de81095 --- /dev/null +++ b/server/ffi.go @@ -0,0 +1,79 @@ +package main + +/* +#cgo CFLAGS: -I ${SRCDIR}/lib +#cgo LDFLAGS: -L ${SRCDIR}/lib -l ffi_wrapper +#include "ffi-wrapper.h" +#include +*/ +import "C" + +// Circles +type Circle struct { + cptr *C.Circle +} + +func NewCircle(x float64, y float64, rad float64) Circle { + return Circle{ + cptr: C.new_circle(C.double(x), C.double(y), C.double(rad)), + } +} + +func (c *Circle) UpdatePos(x float64, y float64) { + c.cptr.x = C.double(x) + c.cptr.y = C.double(y) +} + +func DestroyCircle(circle Circle) { + if circle.cptr == nil { + return + } + + C.destroy_circle(circle.cptr) +} + +func (c *Circle) GetPos() (float64, float64) { + return float64(c.cptr.x), float64(c.cptr.y) +} + +// Bullets +type Bullet struct { + cptr *C.Bullet +} + +func NewLinearBullet(tick int64, spawnX float64, spawnY float64, radius float64, velX float64, velY float64) Bullet { + return Bullet{ + cptr: C.new_bullet( + C.uint8_t(0), + C.int64_t(tick), + C.double(spawnX), + C.double(spawnY), + C.double(radius), + C.double(velX), + C.double(velY), + ), + } +} + +func DestroyBullet(bullet Bullet) { + if bullet.cptr == nil { + return + } + + C.destroy_bullet(bullet.cptr) +} + +func (b *Bullet) BeyondKillBoundary(tick int64) bool { + return bool(C.bullet_beyond_kill_boundary(b.cptr, C.int64_t(tick))) +} + +func (b *Bullet) CollidesWith(circle Circle, tick int64) bool { + return bool(C.bullet_collides_with(b.cptr, C.int64_t(tick), circle.cptr)) +} + +func (b *Bullet) GetPos(tick int64) (float64, float64) { + var x, y C.double + C.bullet_get_current_pos(b.cptr, C.int64_t(tick), &x, &y) + + return float64(x), float64(y) +} diff --git a/server/main.go b/server/main.go index 5ded290..941828f 100644 --- a/server/main.go +++ b/server/main.go @@ -1,13 +1,5 @@ package main -/* -#cgo CFLAGS: -I ${SRCDIR}/lib -#cgo LDFLAGS: -L ${SRCDIR}/lib -l ffi_wrapper -#include "ffi-wrapper.h" -#include -*/ -import "C" - import ( "context" "database/sql" @@ -44,9 +36,9 @@ const ( type BattleRoyaleMatch struct{} type PlayerStageState struct { - hitCol *C.Circle - grazeCol *C.Circle - bullets []*C.Bullet + hitCol Circle + grazeCol Circle + bullets []Bullet updatePlayerPos bool health int graze int @@ -120,9 +112,9 @@ func (m *BattleRoyaleMatch) MatchJoin(ctx context.Context, logger runtime.Logger lobbyState.presences[presences[i].GetSessionId()] = &PresenceState{ presence: presences[i], stageState: PlayerStageState{ - hitCol: C.new_circle(C.double(STAGE_WIDTH*0.5), C.double(STAGE_HEIGHT-STAGE_HEIGHT*0.1), C.double(STAGE_WIDTH*PLAYER_HIT_COL_RADIUS_MULTIPLIER)), - grazeCol: C.new_circle(C.double(STAGE_WIDTH*0.5), C.double(STAGE_HEIGHT-STAGE_HEIGHT*0.1), C.double(STAGE_WIDTH*PLAYER_GRAZE_COL_RADIUS_MULTIPLIER)), - bullets: []*C.Bullet{}, + hitCol: NewCircle(STAGE_WIDTH*0.5, STAGE_HEIGHT-STAGE_HEIGHT*0.1, STAGE_WIDTH*PLAYER_HIT_COL_RADIUS_MULTIPLIER), + grazeCol: NewCircle(STAGE_WIDTH*0.5, STAGE_HEIGHT-STAGE_HEIGHT*0.1, STAGE_WIDTH*PLAYER_GRAZE_COL_RADIUS_MULTIPLIER), + bullets: []Bullet{}, updatePlayerPos: true, health: 3, deathTimer: -1, @@ -147,10 +139,10 @@ func (m *BattleRoyaleMatch) MatchLeave(ctx context.Context, logger runtime.Logge playerState, exists := lobbyState.presences[sessionID] if exists { for _, bullet := range playerState.stageState.bullets { - C.destroy_bullet(bullet) + DestroyBullet(bullet) } - C.destroy_circle(playerState.stageState.hitCol) - C.destroy_circle(playerState.stageState.grazeCol) + DestroyCircle(playerState.stageState.hitCol) + DestroyCircle(playerState.stageState.grazeCol) delete(lobbyState.presences, sessionID) } } @@ -222,11 +214,8 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger update.X = math.Max(0, math.Min(update.X, STAGE_WIDTH)) update.Y = math.Max(0, math.Min(update.Y, STAGE_HEIGHT)) - lobbyState.presences[msg.GetSessionId()].stageState.hitCol.x = C.double(update.X) - lobbyState.presences[msg.GetSessionId()].stageState.hitCol.y = C.double(update.Y) - - lobbyState.presences[msg.GetSessionId()].stageState.grazeCol.x = C.double(update.X) - lobbyState.presences[msg.GetSessionId()].stageState.grazeCol.y = C.double(update.Y) + lobbyState.presences[msg.GetSessionId()].stageState.hitCol.UpdatePos(update.X, update.Y) + lobbyState.presences[msg.GetSessionId()].stageState.grazeCol.UpdatePos(update.X, update.Y) if clampedX || clampedY { lobbyState.presences[msg.GetSessionId()].stageState.updatePlayerPos = true @@ -236,9 +225,9 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger // Compute and broadcast per-presence state for _, v := range lobbyState.presences { // Clean up bullets when they pass off the board - v.stageState.bullets = slices.DeleteFunc(v.stageState.bullets, func(b *C.Bullet) bool { - if C.bullet_beyond_kill_boundary(b, C.int64_t(tick)) { - C.destroy_bullet(b) + v.stageState.bullets = slices.DeleteFunc(v.stageState.bullets, func(b Bullet) bool { + if b.BeyondKillBoundary(tick) { + DestroyBullet(b) return true } return false @@ -250,20 +239,16 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger } if v.stageState.deathTimer == 0 { // If the player's death timer has run out, reset them. 0 is a special deathTimer tick that indicates reset to the clients. - v.stageState.hitCol.x = C.double(STAGE_WIDTH * 0.5) - v.stageState.hitCol.y = C.double(STAGE_HEIGHT - STAGE_HEIGHT*0.1) - - v.stageState.grazeCol.x = C.double(STAGE_WIDTH * 0.5) - v.stageState.grazeCol.y = C.double(STAGE_HEIGHT - STAGE_HEIGHT*0.1) - + v.stageState.hitCol.UpdatePos(STAGE_WIDTH*0.5, STAGE_HEIGHT-STAGE_HEIGHT*0.1) + v.stageState.grazeCol.UpdatePos(STAGE_WIDTH*0.5, STAGE_HEIGHT-STAGE_HEIGHT*0.1) v.stageState.updatePlayerPos = true } else if v.stageState.deathTimer == -1 { // If the player is alive, check if the player collided with a bullet and kill them if so - if slices.ContainsFunc(v.stageState.bullets, func(b *C.Bullet) bool { - return bool(C.bullet_collides_with(b, C.int64_t(tick), v.stageState.hitCol)) + if slices.ContainsFunc(v.stageState.bullets, func(b Bullet) bool { + return b.CollidesWith(v.stageState.hitCol, tick) }) { v.stageState.deathTimer = PLAYER_DEATH_TIMER_MAX - } else if slices.ContainsFunc(v.stageState.bullets, func(b *C.Bullet) bool { // Otherwise, check the graze col and increment the graze and score - return bool(C.bullet_collides_with(b, C.int64_t(tick), v.stageState.grazeCol)) + } else if slices.ContainsFunc(v.stageState.bullets, func(b Bullet) bool { // Otherwise, check the graze col and increment the graze and score + return b.CollidesWith(v.stageState.grazeCol, tick) }) { v.stageState.graze += GRAZE_ADDITION_MULTIPLIER } @@ -289,26 +274,24 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger velx := bulletSpeed * math.Cos(angleRad) vely := bulletSpeed * math.Sin(angleRad) - bullet := C.new_bullet( - C.uint8_t(BULLET_LINEAR), - C.int64_t(tick), - C.double(spawnX), // Fixed X start position - C.double(spawnY), // Fixed Y start position - C.double(bulletRadiusMult*STAGE_WIDTH), // Fixed radius - C.double(velx), - C.double(vely), + bullet := NewLinearBullet( + tick, + spawnX, + spawnY, + bulletRadiusMult*STAGE_WIDTH, + velx, + vely, ) v.stageState.bullets = append(v.stageState.bullets, bullet) - var x, y C.double - C.bullet_get_current_pos(bullet, C.int64_t(tick), &x, &y) + x, y := bullet.GetPos(tick) bulletData := map[string]interface{}{ "class": BULLET_LINEAR, "tick": tick, - "x": float64(x), - "y": float64(y), + "x": x, + "y": y, "radius_multiplier": bulletRadiusMult, "vel_x": velx, "vel_y": vely, @@ -318,16 +301,18 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger } } + hitPosX, hitPosY := v.stageState.hitCol.GetPos() + grazePosX, grazePosY := v.stageState.hitCol.GetPos() var tickData = GameTickUpdate{ Tick: tick, PlayerHitPos: map[string]interface{}{ - "x": v.stageState.hitCol.x, - "y": v.stageState.hitCol.y, + "x": hitPosX, + "y": hitPosY, "radius_multiplier": PLAYER_HIT_COL_RADIUS_MULTIPLIER, }, PlayerGrazePos: map[string]interface{}{ - "x": v.stageState.grazeCol.x, - "y": v.stageState.grazeCol.y, + "x": grazePosX, + "y": grazePosY, "radius_multiplier": PLAYER_GRAZE_COL_RADIUS_MULTIPLIER, }, NewBullets: newBulletsToBroadcast,