fix: deep-copy GameState in GetState to prevent data race

Replace shallow struct copy with full deep copy of Players, Monsters,
Floor/Rooms, Inventory, Relics, ShopItems, and CombatLog slices so
concurrent readers via GetState never alias the combatLoop's live data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-24 10:23:21 +09:00
parent ae3375a023
commit b0766c488c
2 changed files with 83 additions and 1 deletions

View File

@@ -7,6 +7,37 @@ import (
"github.com/tolelom/catacombs/entity"
)
func TestGetStateNoRace(t *testing.T) {
s := NewGameSession()
p := entity.NewPlayer("Racer", entity.ClassWarrior)
s.AddPlayer(p)
s.StartGame()
done := make(chan struct{})
go func() {
defer close(done)
for i := 0; i < 100; i++ {
st := s.GetState()
for _, p := range st.Players {
_ = p.HP
_ = p.Gold
}
for _, m := range st.Monsters {
_ = m.HP
}
}
}()
for i := 0; i < 10; i++ {
select {
case s.actionCh <- playerActionMsg{PlayerName: "Racer", Action: PlayerAction{Type: ActionWait}}:
default:
}
time.Sleep(10 * time.Millisecond)
}
<-done
}
func TestSessionTurnTimeout(t *testing.T) {
s := NewGameSession()
p := entity.NewPlayer("test", entity.ClassWarrior)