Merge pull request 'fix(game): release a banished race's assets during turn generation' (#80) from feature/game-banish-wipe into development
This commit was merged in pull request #80.
This commit is contained in:
+7
-3
@@ -102,9 +102,13 @@ remove-and-banish flow.
|
|||||||
non-empty and must match an existing race in the engine's roster.
|
non-empty and must match an existing race in the engine's roster.
|
||||||
- Successful response: `204 No Content` with an empty body.
|
- Successful response: `204 No Content` with an empty body.
|
||||||
- Error responses follow the same `400` / `500` envelope shape as the
|
- Error responses follow the same `400` / `500` envelope shape as the
|
||||||
other admin endpoints. The engine-side mechanics of `banish` (what
|
other admin endpoints. `banish` only flags the race extinct, so it can
|
||||||
exactly happens to the race's planets, fleets, and pending orders) are
|
no longer submit or have orders applied; its assets are released at the
|
||||||
owned by the engine maintainers.
|
start of the next turn generation (`TurnWipeExtinctRaces`), the same way
|
||||||
|
an idle/quit timeout is handled but without the wait — ship groups and
|
||||||
|
fleets are removed, its planets become uninhabited (the working industry
|
||||||
|
and the capital stockpile are cleared, raw material is retained), and
|
||||||
|
votes cast for it are reset.
|
||||||
|
|
||||||
### `GET /healthz`
|
### `GET /healthz`
|
||||||
|
|
||||||
|
|||||||
@@ -117,13 +117,34 @@ func (c *Cache) raceTechLevel(ri int, t game.Tech, v float64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) TurnWipeExtinctRaces() {
|
func (c *Cache) TurnWipeExtinctRaces() {
|
||||||
for i := range c.listRaceActingIdx() {
|
for i := range c.listRaceIdx() {
|
||||||
if (c.g.Race[i].Extinct && c.g.Race[i].TTL > 0) || (!c.g.Race[i].Extinct && c.g.Race[i].TTL == 0) {
|
r := &c.g.Race[i]
|
||||||
|
// Idle timeout or voluntary quit: a still-active race whose TTL ran
|
||||||
|
// out. Administrative banish: a race already flagged extinct that
|
||||||
|
// still holds assets to release. Once a race is wiped it owns nothing,
|
||||||
|
// so the asset check keeps this idempotent across later turns.
|
||||||
|
if (!r.Extinct && r.TTL == 0) || (r.Extinct && c.raceHasAssets(i)) {
|
||||||
c.wipeRace(i)
|
c.wipeRace(i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// raceHasAssets reports whether the race still owns a planet or a ship group.
|
||||||
|
func (c *Cache) raceHasAssets(ri int) bool {
|
||||||
|
id := c.g.Race[ri].ID
|
||||||
|
for i := range c.g.Map.Planet {
|
||||||
|
if c.g.Map.Planet[i].OwnedBy(id) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range c.g.ShipGroups {
|
||||||
|
if c.g.ShipGroups[i].OwnerID == id {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Cache) wipeRace(ri int) {
|
func (c *Cache) wipeRace(ri int) {
|
||||||
c.validateRaceIndex(ri)
|
c.validateRaceIndex(ri)
|
||||||
r := &c.g.Race[ri]
|
r := &c.g.Race[ri]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package controller_test
|
package controller_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
e "galaxy/error"
|
e "galaxy/error"
|
||||||
@@ -90,3 +91,39 @@ func TestRaceID(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, Race_0_ID, id)
|
assert.Equal(t, Race_0_ID, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestBanishReleasesAssets checks that an administratively banished race only
|
||||||
|
// gets flagged extinct, and its planets and ships are released during turn
|
||||||
|
// generation; a second pass is a no-op.
|
||||||
|
func TestBanishReleasesAssets(t *testing.T) {
|
||||||
|
c, g := newCache()
|
||||||
|
c.CreateShipsUnsafe_T(Race_1_idx, c.MustShipClass(Race_1_idx, Race_1_Gunship).ID, R1_Planet_1_num, 3)
|
||||||
|
assert.True(t, c.MustPlanet(R1_Planet_1_num).OwnedBy(Race_1_ID))
|
||||||
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1)
|
||||||
|
|
||||||
|
assert.NoError(t, g.RaceBanish(Race_1.Name))
|
||||||
|
assert.True(t, c.Race(Race_1_idx).Extinct)
|
||||||
|
assert.True(t, c.MustPlanet(R1_Planet_1_num).OwnedBy(Race_1_ID), "still owned until the turn runs")
|
||||||
|
|
||||||
|
c.TurnWipeExtinctRaces()
|
||||||
|
assert.False(t, c.MustPlanet(R1_Planet_1_num).Owned())
|
||||||
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 0)
|
||||||
|
|
||||||
|
// Idempotent: re-running over an already-wiped (asset-less) race is a no-op.
|
||||||
|
c.TurnWipeExtinctRaces()
|
||||||
|
assert.False(t, c.MustPlanet(R1_Planet_1_num).Owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestIdleRaceWipedOnTimeout guards that a still-active race whose TTL ran out
|
||||||
|
// (idle timeout or quit) is still wiped after the iterator change.
|
||||||
|
func TestIdleRaceWipedOnTimeout(t *testing.T) {
|
||||||
|
c, _ := newCache()
|
||||||
|
c.CreateShipsUnsafe_T(Race_1_idx, c.MustShipClass(Race_1_idx, Race_1_Gunship).ID, R1_Planet_1_num, 1)
|
||||||
|
c.Race(Race_1_idx).TTL = 0
|
||||||
|
assert.False(t, c.Race(Race_1_idx).Extinct)
|
||||||
|
|
||||||
|
c.TurnWipeExtinctRaces()
|
||||||
|
assert.True(t, c.Race(Race_1_idx).Extinct)
|
||||||
|
assert.False(t, c.MustPlanet(R1_Planet_1_num).Owned())
|
||||||
|
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 0)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user