Archived
1
0
Fork 0

cgo wrapper file

This commit is contained in:
Sebastian Benjamin 2025-04-22 18:15:22 -07:00
parent c41604e35b
commit 234b1df8f3
4 changed files with 117 additions and 53 deletions

View file

@ -1,6 +1,6 @@
[gd_scene load_steps=5 format=3 uid="uid://dsiowq0rnacln"] [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://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://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"] [ext_resource type="Script" uid="uid://ddc5iqgtyv2ns" path="res://danmaku!/BulletManager.gd" id="4_ubrrh"]

View file

@ -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="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"] [ext_resource type="Texture2D" uid="uid://bs3fntlmlqpt2" path="res://icon.svg" id="2_04s0l"]

79
server/ffi.go Normal file
View file

@ -0,0 +1,79 @@
package main
/*
#cgo CFLAGS: -I ${SRCDIR}/lib
#cgo LDFLAGS: -L ${SRCDIR}/lib -l ffi_wrapper
#include "ffi-wrapper.h"
#include <stdlib.h>
*/
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)
}

View file

@ -1,13 +1,5 @@
package main package main
/*
#cgo CFLAGS: -I ${SRCDIR}/lib
#cgo LDFLAGS: -L ${SRCDIR}/lib -l ffi_wrapper
#include "ffi-wrapper.h"
#include <stdlib.h>
*/
import "C"
import ( import (
"context" "context"
"database/sql" "database/sql"
@ -44,9 +36,9 @@ const (
type BattleRoyaleMatch struct{} type BattleRoyaleMatch struct{}
type PlayerStageState struct { type PlayerStageState struct {
hitCol *C.Circle hitCol Circle
grazeCol *C.Circle grazeCol Circle
bullets []*C.Bullet bullets []Bullet
updatePlayerPos bool updatePlayerPos bool
health int health int
graze int graze int
@ -120,9 +112,9 @@ func (m *BattleRoyaleMatch) MatchJoin(ctx context.Context, logger runtime.Logger
lobbyState.presences[presences[i].GetSessionId()] = &PresenceState{ lobbyState.presences[presences[i].GetSessionId()] = &PresenceState{
presence: presences[i], presence: presences[i],
stageState: PlayerStageState{ 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)), hitCol: NewCircle(STAGE_WIDTH*0.5, STAGE_HEIGHT-STAGE_HEIGHT*0.1, 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)), grazeCol: NewCircle(STAGE_WIDTH*0.5, STAGE_HEIGHT-STAGE_HEIGHT*0.1, STAGE_WIDTH*PLAYER_GRAZE_COL_RADIUS_MULTIPLIER),
bullets: []*C.Bullet{}, bullets: []Bullet{},
updatePlayerPos: true, updatePlayerPos: true,
health: 3, health: 3,
deathTimer: -1, deathTimer: -1,
@ -147,10 +139,10 @@ func (m *BattleRoyaleMatch) MatchLeave(ctx context.Context, logger runtime.Logge
playerState, exists := lobbyState.presences[sessionID] playerState, exists := lobbyState.presences[sessionID]
if exists { if exists {
for _, bullet := range playerState.stageState.bullets { for _, bullet := range playerState.stageState.bullets {
C.destroy_bullet(bullet) DestroyBullet(bullet)
} }
C.destroy_circle(playerState.stageState.hitCol) DestroyCircle(playerState.stageState.hitCol)
C.destroy_circle(playerState.stageState.grazeCol) DestroyCircle(playerState.stageState.grazeCol)
delete(lobbyState.presences, sessionID) 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.X = math.Max(0, math.Min(update.X, STAGE_WIDTH))
update.Y = math.Max(0, math.Min(update.Y, STAGE_HEIGHT)) 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.UpdatePos(update.X, update.Y)
lobbyState.presences[msg.GetSessionId()].stageState.hitCol.y = C.double(update.Y) lobbyState.presences[msg.GetSessionId()].stageState.grazeCol.UpdatePos(update.X, update.Y)
lobbyState.presences[msg.GetSessionId()].stageState.grazeCol.x = C.double(update.X)
lobbyState.presences[msg.GetSessionId()].stageState.grazeCol.y = C.double(update.Y)
if clampedX || clampedY { if clampedX || clampedY {
lobbyState.presences[msg.GetSessionId()].stageState.updatePlayerPos = true 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 // Compute and broadcast per-presence state
for _, v := range lobbyState.presences { for _, v := range lobbyState.presences {
// Clean up bullets when they pass off the board // Clean up bullets when they pass off the board
v.stageState.bullets = slices.DeleteFunc(v.stageState.bullets, func(b *C.Bullet) bool { v.stageState.bullets = slices.DeleteFunc(v.stageState.bullets, func(b Bullet) bool {
if C.bullet_beyond_kill_boundary(b, C.int64_t(tick)) { if b.BeyondKillBoundary(tick) {
C.destroy_bullet(b) DestroyBullet(b)
return true return true
} }
return false 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. 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.UpdatePos(STAGE_WIDTH*0.5, STAGE_HEIGHT-STAGE_HEIGHT*0.1)
v.stageState.hitCol.y = C.double(STAGE_HEIGHT - STAGE_HEIGHT*0.1) v.stageState.grazeCol.UpdatePos(STAGE_WIDTH*0.5, 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.updatePlayerPos = true 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 } 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 { if slices.ContainsFunc(v.stageState.bullets, func(b Bullet) bool {
return bool(C.bullet_collides_with(b, C.int64_t(tick), v.stageState.hitCol)) return b.CollidesWith(v.stageState.hitCol, tick)
}) { }) {
v.stageState.deathTimer = PLAYER_DEATH_TIMER_MAX 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 } else if slices.ContainsFunc(v.stageState.bullets, func(b 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)) return b.CollidesWith(v.stageState.grazeCol, tick)
}) { }) {
v.stageState.graze += GRAZE_ADDITION_MULTIPLIER 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) velx := bulletSpeed * math.Cos(angleRad)
vely := bulletSpeed * math.Sin(angleRad) vely := bulletSpeed * math.Sin(angleRad)
bullet := C.new_bullet( bullet := NewLinearBullet(
C.uint8_t(BULLET_LINEAR), tick,
C.int64_t(tick), spawnX,
C.double(spawnX), // Fixed X start position spawnY,
C.double(spawnY), // Fixed Y start position bulletRadiusMult*STAGE_WIDTH,
C.double(bulletRadiusMult*STAGE_WIDTH), // Fixed radius velx,
C.double(velx), vely,
C.double(vely),
) )
v.stageState.bullets = append(v.stageState.bullets, bullet) v.stageState.bullets = append(v.stageState.bullets, bullet)
var x, y C.double x, y := bullet.GetPos(tick)
C.bullet_get_current_pos(bullet, C.int64_t(tick), &x, &y)
bulletData := map[string]interface{}{ bulletData := map[string]interface{}{
"class": BULLET_LINEAR, "class": BULLET_LINEAR,
"tick": tick, "tick": tick,
"x": float64(x), "x": x,
"y": float64(y), "y": y,
"radius_multiplier": bulletRadiusMult, "radius_multiplier": bulletRadiusMult,
"vel_x": velx, "vel_x": velx,
"vel_y": vely, "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{ var tickData = GameTickUpdate{
Tick: tick, Tick: tick,
PlayerHitPos: map[string]interface{}{ PlayerHitPos: map[string]interface{}{
"x": v.stageState.hitCol.x, "x": hitPosX,
"y": v.stageState.hitCol.y, "y": hitPosY,
"radius_multiplier": PLAYER_HIT_COL_RADIUS_MULTIPLIER, "radius_multiplier": PLAYER_HIT_COL_RADIUS_MULTIPLIER,
}, },
PlayerGrazePos: map[string]interface{}{ PlayerGrazePos: map[string]interface{}{
"x": v.stageState.grazeCol.x, "x": grazePosX,
"y": v.stageState.grazeCol.y, "y": grazePosY,
"radius_multiplier": PLAYER_GRAZE_COL_RADIUS_MULTIPLIER, "radius_multiplier": PLAYER_GRAZE_COL_RADIUS_MULTIPLIER,
}, },
NewBullets: newBulletsToBroadcast, NewBullets: newBulletsToBroadcast,