From 79461030d3dde851c5d6c73040acc29b548ce1e8 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 21 Nov 2021 10:44:06 +0100 Subject: [PATCH 01/51] feat: add phase components representing states --- .../components/CountdownPhaseComponent.java | 10 ++++++++++ .../lightandshadow/components/GamePhaseComponent.java | 10 ++++++++++ .../lightandshadow/components/IdlePhaseComponent.java | 10 ++++++++++ .../components/PostgamePhaseComponent.java | 10 ++++++++++ .../components/PregamePhaseComponent.java | 10 ++++++++++ 5 files changed, 50 insertions(+) create mode 100644 src/main/java/org/terasology/module/lightandshadow/components/CountdownPhaseComponent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/components/GamePhaseComponent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/components/IdlePhaseComponent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/components/PostgamePhaseComponent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/components/PregamePhaseComponent.java diff --git a/src/main/java/org/terasology/module/lightandshadow/components/CountdownPhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/CountdownPhaseComponent.java new file mode 100644 index 00000000..8267f27c --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/components/CountdownPhaseComponent.java @@ -0,0 +1,10 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.components; + +import org.terasology.gestalt.entitysystem.component.EmptyComponent; + +// TODO: Would it make sense to create a parent class `PhaseComponent` here? +public class CountdownPhaseComponent extends EmptyComponent { +} diff --git a/src/main/java/org/terasology/module/lightandshadow/components/GamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/GamePhaseComponent.java new file mode 100644 index 00000000..5e59f141 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/components/GamePhaseComponent.java @@ -0,0 +1,10 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.components; + +import org.terasology.gestalt.entitysystem.component.EmptyComponent; + +// TODO: Would it make sense to create a parent class `PhaseComponent` here? +public class GamePhaseComponent extends EmptyComponent { +} diff --git a/src/main/java/org/terasology/module/lightandshadow/components/IdlePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/IdlePhaseComponent.java new file mode 100644 index 00000000..550d3298 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/components/IdlePhaseComponent.java @@ -0,0 +1,10 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.components; + +import org.terasology.gestalt.entitysystem.component.EmptyComponent; + +// TODO: Would it make sense to create a parent class `PhaseComponent` here? +public class IdlePhaseComponent extends EmptyComponent { +} diff --git a/src/main/java/org/terasology/module/lightandshadow/components/PostgamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/PostgamePhaseComponent.java new file mode 100644 index 00000000..4e30fa20 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/components/PostgamePhaseComponent.java @@ -0,0 +1,10 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.components; + +import org.terasology.gestalt.entitysystem.component.EmptyComponent; + +// TODO: Would it make sense to create a parent class `PhaseComponent` here? +public class PostgamePhaseComponent extends EmptyComponent { +} diff --git a/src/main/java/org/terasology/module/lightandshadow/components/PregamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/PregamePhaseComponent.java new file mode 100644 index 00000000..95dc3587 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/components/PregamePhaseComponent.java @@ -0,0 +1,10 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.components; + +import org.terasology.gestalt.entitysystem.component.EmptyComponent; + +// TODO: Would it make sense to create a parent class `PhaseComponent` here? +public class PregamePhaseComponent extends EmptyComponent { +} From 518119fd6966e9a6aa5dd658b78a51fb52b4e8af Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 21 Nov 2021 10:44:43 +0100 Subject: [PATCH 02/51] feat: add phase system to manage state machine --- .../lightandshadow/systems/PhaseSystem.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/main/java/org/terasology/module/lightandshadow/systems/PhaseSystem.java diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/PhaseSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/PhaseSystem.java new file mode 100644 index 00000000..fd33a6e3 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/systems/PhaseSystem.java @@ -0,0 +1,62 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.systems; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terasology.engine.entitySystem.entity.EntityManager; +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.systems.BaseComponentSystem; +import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.registry.In; +import org.terasology.engine.registry.Share; +import org.terasology.module.lightandshadow.components.CountdownPhaseComponent; +import org.terasology.module.lightandshadow.components.GamePhaseComponent; +import org.terasology.module.lightandshadow.components.IdlePhaseComponent; +import org.terasology.module.lightandshadow.components.PostgamePhaseComponent; +import org.terasology.module.lightandshadow.components.PregamePhaseComponent; + +/** + * Provides an entity that keeps track of game state information. + */ +@RegisterSystem +@Share(value = PhaseSystem.class) +public class PhaseSystem extends BaseComponentSystem { + + private static final Logger logger = LoggerFactory.getLogger(PhaseSystem.class); + + @In + EntityManager entityManager; + + @In + private GameEntitySystem gameEntitySystem; + + @Override + public void initialise() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + gameEntity.addOrSaveComponent(new IdlePhaseComponent()); + } + + // TODO: Would it make more sense to ask for the current phase instead or even in addition? + + public boolean isInIdlePhase() { + return gameEntitySystem.getGameEntity().hasComponent(IdlePhaseComponent.class); + } + + public boolean isInPregamePhase() { + return gameEntitySystem.getGameEntity().hasComponent(PregamePhaseComponent.class); + } + + public boolean isInCountdownPhase() { + return gameEntitySystem.getGameEntity().hasComponent(CountdownPhaseComponent.class); + } + + public boolean isInGamePhase() { + return gameEntitySystem.getGameEntity().hasComponent(GamePhaseComponent.class); + } + + public boolean isInPostgamePhase() { + return gameEntitySystem.getGameEntity().hasComponent(PostgamePhaseComponent.class); + } +} From 68a077276c68bdb0ae42bb52c96267cd34385c69 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 21 Nov 2021 10:45:20 +0100 Subject: [PATCH 03/51] feat: add notification events for completed state transitions --- .../events/OnCountdownPhaseStarted.java | 16 ++++++++++++++++ .../events/OnGamePhaseStarted.java | 16 ++++++++++++++++ .../events/OnPostgamePhaseStarted.java | 16 ++++++++++++++++ .../events/OnPregamePhaseStarted.java | 16 ++++++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 src/main/java/org/terasology/module/lightandshadow/events/OnCountdownPhaseStarted.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/events/OnGamePhaseStarted.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/events/OnPostgamePhaseStarted.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/events/OnPregamePhaseStarted.java diff --git a/src/main/java/org/terasology/module/lightandshadow/events/OnCountdownPhaseStarted.java b/src/main/java/org/terasology/module/lightandshadow/events/OnCountdownPhaseStarted.java new file mode 100644 index 00000000..ba005519 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/events/OnCountdownPhaseStarted.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.events; + +import org.terasology.engine.network.BroadcastEvent; +import org.terasology.gestalt.entitysystem.event.Event; + +// TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? + +@BroadcastEvent +public class OnCountdownPhaseStarted implements Event { + + public OnCountdownPhaseStarted() { + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/events/OnGamePhaseStarted.java b/src/main/java/org/terasology/module/lightandshadow/events/OnGamePhaseStarted.java new file mode 100644 index 00000000..cde0ce27 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/events/OnGamePhaseStarted.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.events; + +import org.terasology.engine.network.BroadcastEvent; +import org.terasology.gestalt.entitysystem.event.Event; + +// TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? + +@BroadcastEvent +public class OnGamePhaseStarted implements Event { + + public OnGamePhaseStarted() { + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/events/OnPostgamePhaseStarted.java b/src/main/java/org/terasology/module/lightandshadow/events/OnPostgamePhaseStarted.java new file mode 100644 index 00000000..d28850b8 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/events/OnPostgamePhaseStarted.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.events; + +import org.terasology.engine.network.BroadcastEvent; +import org.terasology.gestalt.entitysystem.event.Event; + +// TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? + +@BroadcastEvent +public class OnPostgamePhaseStarted implements Event { + + public OnPostgamePhaseStarted() { + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/events/OnPregamePhaseStarted.java b/src/main/java/org/terasology/module/lightandshadow/events/OnPregamePhaseStarted.java new file mode 100644 index 00000000..59d04b16 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/events/OnPregamePhaseStarted.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.events; + +import org.terasology.engine.network.BroadcastEvent; +import org.terasology.gestalt.entitysystem.event.Event; + +// TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? + +@BroadcastEvent +public class OnPregamePhaseStarted implements Event { + + public OnPregamePhaseStarted() { + } +} From ffb5e6b8f9dc769d9c86680e937e98fa20b6dd79 Mon Sep 17 00:00:00 2001 From: Josephine 'Niruandaleth' Rueckert Date: Fri, 17 Dec 2021 21:09:09 +0100 Subject: [PATCH 04/51] feat: add intermediate changes --- .../components/CountdownPhaseComponent.java | 10 - .../components/GamePhaseComponent.java | 10 - .../components/IdlePhaseComponent.java | 10 - .../components/PostgamePhaseComponent.java | 10 - .../components/PregamePhaseComponent.java | 10 - .../statemachine/CountdownPhaseComponent.java | 16 ++ .../statemachine/GamePhaseComponent.java | 16 ++ .../statemachine/IdlePhaseComponent.java | 16 ++ .../OnCountdownPhaseEndedEvent.java | 16 ++ .../OnCountdownPhaseStartedEvent.java | 16 ++ .../OnGamePhaseEndedEvent.java} | 8 +- .../OnGamePhaseStartedEvent.java} | 8 +- .../OnIdlePhaseEndedEvent.java} | 8 +- .../OnIdlePhaseStartedEvent.java} | 8 +- .../OnPostgamePhaseEndedEvent.java | 16 ++ .../OnPostgamePhaseStartedEvent.java | 16 ++ .../OnPregamePhaseEndedEvent.java | 16 ++ .../OnPregamePhaseStartedEvent.java | 16 ++ .../statemachine/PhaseCommandsSystem.java | 52 +++++ .../statemachine/PhaseComponent.java | 11 + .../statemachine/PhaseSystem.java | 208 ++++++++++++++++++ .../statemachine/PostgamePhaseComponent.java | 16 ++ .../statemachine/PregamePhaseComponent.java | 16 ++ .../lightandshadow/systems/PhaseSystem.java | 62 ------ .../statemachine/PhaseCommandsSystemTest.java | 44 ++++ .../statemachine/PhaseSystemTest.java | 41 ++++ 26 files changed, 548 insertions(+), 128 deletions(-) delete mode 100644 src/main/java/org/terasology/module/lightandshadow/components/CountdownPhaseComponent.java delete mode 100644 src/main/java/org/terasology/module/lightandshadow/components/GamePhaseComponent.java delete mode 100644 src/main/java/org/terasology/module/lightandshadow/components/IdlePhaseComponent.java delete mode 100644 src/main/java/org/terasology/module/lightandshadow/components/PostgamePhaseComponent.java delete mode 100644 src/main/java/org/terasology/module/lightandshadow/components/PregamePhaseComponent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/CountdownPhaseComponent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/GamePhaseComponent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/IdlePhaseComponent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseEndedEvent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseStartedEvent.java rename src/main/java/org/terasology/module/lightandshadow/{events/OnCountdownPhaseStarted.java => statemachine/OnGamePhaseEndedEvent.java} (56%) rename src/main/java/org/terasology/module/lightandshadow/{events/OnPostgamePhaseStarted.java => statemachine/OnGamePhaseStartedEvent.java} (55%) rename src/main/java/org/terasology/module/lightandshadow/{events/OnGamePhaseStarted.java => statemachine/OnIdlePhaseEndedEvent.java} (56%) rename src/main/java/org/terasology/module/lightandshadow/{events/OnPregamePhaseStarted.java => statemachine/OnIdlePhaseStartedEvent.java} (55%) create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseEndedEvent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseStartedEvent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseEndedEvent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseStartedEvent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystem.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseComponent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseSystem.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/PostgamePhaseComponent.java create mode 100644 src/main/java/org/terasology/module/lightandshadow/statemachine/PregamePhaseComponent.java delete mode 100644 src/main/java/org/terasology/module/lightandshadow/systems/PhaseSystem.java create mode 100644 src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystemTest.java create mode 100644 src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java diff --git a/src/main/java/org/terasology/module/lightandshadow/components/CountdownPhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/CountdownPhaseComponent.java deleted file mode 100644 index 8267f27c..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/components/CountdownPhaseComponent.java +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.components; - -import org.terasology.gestalt.entitysystem.component.EmptyComponent; - -// TODO: Would it make sense to create a parent class `PhaseComponent` here? -public class CountdownPhaseComponent extends EmptyComponent { -} diff --git a/src/main/java/org/terasology/module/lightandshadow/components/GamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/GamePhaseComponent.java deleted file mode 100644 index 5e59f141..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/components/GamePhaseComponent.java +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.components; - -import org.terasology.gestalt.entitysystem.component.EmptyComponent; - -// TODO: Would it make sense to create a parent class `PhaseComponent` here? -public class GamePhaseComponent extends EmptyComponent { -} diff --git a/src/main/java/org/terasology/module/lightandshadow/components/IdlePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/IdlePhaseComponent.java deleted file mode 100644 index 550d3298..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/components/IdlePhaseComponent.java +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.components; - -import org.terasology.gestalt.entitysystem.component.EmptyComponent; - -// TODO: Would it make sense to create a parent class `PhaseComponent` here? -public class IdlePhaseComponent extends EmptyComponent { -} diff --git a/src/main/java/org/terasology/module/lightandshadow/components/PostgamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/PostgamePhaseComponent.java deleted file mode 100644 index 4e30fa20..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/components/PostgamePhaseComponent.java +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.components; - -import org.terasology.gestalt.entitysystem.component.EmptyComponent; - -// TODO: Would it make sense to create a parent class `PhaseComponent` here? -public class PostgamePhaseComponent extends EmptyComponent { -} diff --git a/src/main/java/org/terasology/module/lightandshadow/components/PregamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/PregamePhaseComponent.java deleted file mode 100644 index 95dc3587..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/components/PregamePhaseComponent.java +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.components; - -import org.terasology.gestalt.entitysystem.component.EmptyComponent; - -// TODO: Would it make sense to create a parent class `PhaseComponent` here? -public class PregamePhaseComponent extends EmptyComponent { -} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/CountdownPhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/CountdownPhaseComponent.java new file mode 100644 index 00000000..7b1515ec --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/CountdownPhaseComponent.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +public class CountdownPhaseComponent implements PhaseComponent { + + public final PhaseSystem.Phase toPhase() { + return PhaseSystem.Phase.COUNTDOWN; + } + + @Override + public void copyFrom(PhaseComponent other) { + + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/GamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/GamePhaseComponent.java new file mode 100644 index 00000000..dac0ca15 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/GamePhaseComponent.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +public class GamePhaseComponent implements PhaseComponent { + + public final PhaseSystem.Phase toPhase() { + return PhaseSystem.Phase.GAME; + } + + @Override + public void copyFrom(PhaseComponent other) { + + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/IdlePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/IdlePhaseComponent.java new file mode 100644 index 00000000..faf8f54e --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/IdlePhaseComponent.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +public class IdlePhaseComponent implements PhaseComponent { + + public final PhaseSystem.Phase toPhase() { + return PhaseSystem.Phase.IDLE; + } + + @Override + public void copyFrom(PhaseComponent other) { + + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseEndedEvent.java new file mode 100644 index 00000000..ca376830 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseEndedEvent.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +import org.terasology.engine.entitySystem.event.Event; +import org.terasology.engine.network.BroadcastEvent; + +// TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? + +@BroadcastEvent +public class OnCountdownPhaseEndedEvent implements Event { + + public OnCountdownPhaseEndedEvent() { + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseStartedEvent.java new file mode 100644 index 00000000..a96d7092 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseStartedEvent.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +import org.terasology.engine.network.BroadcastEvent; +import org.terasology.engine.entitySystem.event.Event; + +// TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? + +@BroadcastEvent +public class OnCountdownPhaseStartedEvent implements Event { + + public OnCountdownPhaseStartedEvent() { + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/events/OnCountdownPhaseStarted.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseEndedEvent.java similarity index 56% rename from src/main/java/org/terasology/module/lightandshadow/events/OnCountdownPhaseStarted.java rename to src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseEndedEvent.java index ba005519..26ef85e9 100644 --- a/src/main/java/org/terasology/module/lightandshadow/events/OnCountdownPhaseStarted.java +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseEndedEvent.java @@ -1,16 +1,16 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.events; +package org.terasology.module.lightandshadow.statemachine; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.gestalt.entitysystem.event.Event; +import org.terasology.engine.entitySystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? @BroadcastEvent -public class OnCountdownPhaseStarted implements Event { +public class OnGamePhaseEndedEvent implements Event { - public OnCountdownPhaseStarted() { + public OnGamePhaseEndedEvent() { } } diff --git a/src/main/java/org/terasology/module/lightandshadow/events/OnPostgamePhaseStarted.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseStartedEvent.java similarity index 55% rename from src/main/java/org/terasology/module/lightandshadow/events/OnPostgamePhaseStarted.java rename to src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseStartedEvent.java index d28850b8..e9ab7184 100644 --- a/src/main/java/org/terasology/module/lightandshadow/events/OnPostgamePhaseStarted.java +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseStartedEvent.java @@ -1,16 +1,16 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.events; +package org.terasology.module.lightandshadow.statemachine; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.gestalt.entitysystem.event.Event; +import org.terasology.engine.entitySystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? @BroadcastEvent -public class OnPostgamePhaseStarted implements Event { +public class OnGamePhaseStartedEvent implements Event { - public OnPostgamePhaseStarted() { + public OnGamePhaseStartedEvent() { } } diff --git a/src/main/java/org/terasology/module/lightandshadow/events/OnGamePhaseStarted.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseEndedEvent.java similarity index 56% rename from src/main/java/org/terasology/module/lightandshadow/events/OnGamePhaseStarted.java rename to src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseEndedEvent.java index cde0ce27..f9395c7f 100644 --- a/src/main/java/org/terasology/module/lightandshadow/events/OnGamePhaseStarted.java +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseEndedEvent.java @@ -1,16 +1,16 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.events; +package org.terasology.module.lightandshadow.statemachine; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.gestalt.entitysystem.event.Event; +import org.terasology.engine.entitySystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? @BroadcastEvent -public class OnGamePhaseStarted implements Event { +public class OnIdlePhaseEndedEvent implements Event { - public OnGamePhaseStarted() { + public OnIdlePhaseEndedEvent() { } } diff --git a/src/main/java/org/terasology/module/lightandshadow/events/OnPregamePhaseStarted.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseStartedEvent.java similarity index 55% rename from src/main/java/org/terasology/module/lightandshadow/events/OnPregamePhaseStarted.java rename to src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseStartedEvent.java index 59d04b16..aaf170d5 100644 --- a/src/main/java/org/terasology/module/lightandshadow/events/OnPregamePhaseStarted.java +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseStartedEvent.java @@ -1,16 +1,16 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.events; +package org.terasology.module.lightandshadow.statemachine; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.gestalt.entitysystem.event.Event; +import org.terasology.engine.entitySystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? @BroadcastEvent -public class OnPregamePhaseStarted implements Event { +public class OnIdlePhaseStartedEvent implements Event { - public OnPregamePhaseStarted() { + public OnIdlePhaseStartedEvent() { } } diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseEndedEvent.java new file mode 100644 index 00000000..de56a1d7 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseEndedEvent.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +import org.terasology.engine.network.BroadcastEvent; +import org.terasology.engine.entitySystem.event.Event; + +// TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? + +@BroadcastEvent +public class OnPostgamePhaseEndedEvent implements Event { + + public OnPostgamePhaseEndedEvent() { + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseStartedEvent.java new file mode 100644 index 00000000..cd075ade --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseStartedEvent.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +import org.terasology.engine.network.BroadcastEvent; +import org.terasology.engine.entitySystem.event.Event; + +// TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? + +@BroadcastEvent +public class OnPostgamePhaseStartedEvent implements Event { + + public OnPostgamePhaseStartedEvent() { + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseEndedEvent.java new file mode 100644 index 00000000..23d1d918 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseEndedEvent.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +import org.terasology.engine.entitySystem.event.Event; +import org.terasology.engine.network.BroadcastEvent; + +// TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? + +@BroadcastEvent +public class OnPregamePhaseEndedEvent implements Event { + + public OnPregamePhaseEndedEvent() { + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseStartedEvent.java new file mode 100644 index 00000000..f7dddce2 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseStartedEvent.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +import org.terasology.engine.network.BroadcastEvent; +import org.terasology.engine.entitySystem.event.Event; + +// TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? + +@BroadcastEvent +public class OnPregamePhaseStartedEvent implements Event { + + public OnPregamePhaseStartedEvent() { + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystem.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystem.java new file mode 100644 index 00000000..e0c99f54 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystem.java @@ -0,0 +1,52 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.systems.BaseComponentSystem; +import org.terasology.engine.logic.console.commandSystem.annotations.Command; +import org.terasology.engine.logic.console.commandSystem.annotations.CommandParam; +import org.terasology.engine.logic.console.commandSystem.annotations.Sender; +import org.terasology.engine.logic.permission.PermissionManager; +import org.terasology.engine.registry.In; +import org.terasology.module.inventory.components.ItemCommands; + +public class PhaseCommandsSystem extends BaseComponentSystem { + + private static final Logger logger = LoggerFactory.getLogger(ItemCommands.class); + + @In + PhaseSystem phaseSystem; + + @Command(shortDescription = "Forces a phase transition to a specified target phase", + helpText = "Puts the Light & Shadow World into a desired phase", + runOnServer = true, + requiredPermission = PermissionManager.CHEAT_PERMISSION) + public String forcePhase( + @Sender EntityRef client, + @CommandParam("targetPhase") PhaseSystem.Phase targetPhase) { + + switch(targetPhase) { + case IDLE: + case PREGAME: + case COUNTDOWN: + case GAME: + case POSTGAME: + break; + default: + StringBuilder errorMsg = new StringBuilder(targetPhase + " is not a valid phase, please choose one of the following:"); + for (PhaseSystem.Phase validPhase : PhaseSystem.Phase.values()) { + errorMsg.append(" ").append(validPhase.toString()); + } + logger.error(errorMsg.toString()); + return errorMsg.toString(); + } + + PhaseSystem.Phase currentPhase = phaseSystem.getCurrentPhase(); + phaseSystem.transitionPhase(currentPhase, targetPhase); + return "Initiated phase transition from " + currentPhase + " to " + targetPhase; + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseComponent.java new file mode 100644 index 00000000..ca3bb5ff --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseComponent.java @@ -0,0 +1,11 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +import org.terasology.gestalt.entitysystem.component.Component; + +public interface PhaseComponent extends Component { + + public PhaseSystem.Phase toPhase(); +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseSystem.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseSystem.java new file mode 100644 index 00000000..57981a24 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseSystem.java @@ -0,0 +1,208 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terasology.engine.entitySystem.entity.EntityManager; +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.systems.BaseComponentSystem; +import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.registry.In; +import org.terasology.engine.registry.Share; +import org.terasology.gestalt.naming.Name; +import org.terasology.module.lightandshadow.systems.GameEntitySystem; + +/** + * Provides an entity that keeps track of game state information. + */ +@RegisterSystem +@Share(value = PhaseSystem.class) +public class PhaseSystem extends BaseComponentSystem { + + private static final Logger logger = LoggerFactory.getLogger(PhaseSystem.class); + + @In + GameEntitySystem gameEntitySystem; + + enum Phase { + IDLE(new Name("Idle")), + PREGAME(new Name("Pregame")), + COUNTDOWN(new Name("Countdown")), + GAME(new Name("Game")), + POSTGAME(new Name("Postgame")); + + private final Name phaseName; + + Phase(final Name phaseName) { + this.phaseName = phaseName; + } + + @Override + public final String toString() { + return phaseName.toString(); + } + + public Phase fromString(String phaseString) { + Name input = new Name(phaseString); + switch (input.toString()) { + case IDLE.phaseName.toString(): + } + } + } + + @Override + public void initialise() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + gameEntity.addOrSaveComponent(new IdlePhaseComponent()); + } + + // TODO: What happens here if there's multiple components present that implement PhaseComponent? + public Phase getCurrentPhase() { + return gameEntitySystem.getGameEntity().getComponent(PhaseComponent.class).toPhase(); + } + + public boolean isInIdlePhase() { + return gameEntitySystem.getGameEntity().hasComponent(IdlePhaseComponent.class); + } + + public boolean isInPregamePhase() { + return gameEntitySystem.getGameEntity().hasComponent(PregamePhaseComponent.class); + } + + public boolean isInCountdownPhase() { + return gameEntitySystem.getGameEntity().hasComponent(CountdownPhaseComponent.class); + } + + public boolean isInGamePhase() { + return gameEntitySystem.getGameEntity().hasComponent(GamePhaseComponent.class); + } + + public boolean isInPostgamePhase() { + return gameEntitySystem.getGameEntity().hasComponent(PostgamePhaseComponent.class); + } + + void transitionPhase(Phase from, Phase to) { + logger.debug("Transitioning phases from " + from.toString() + " to " + to.toString()); + switch (from) { + case IDLE: + endIdlePhase(); + break; + case PREGAME: + endPregamePhase(); + break; + case COUNTDOWN: + endCountdownPhase(); + break; + case GAME: + endGamePhase(); + break; + case POSTGAME: + endPostgamePhase(); + break; + default: + logger.debug("Attempted to transition from invalid phase " + to.toString()); + } + + switch (to) { + case IDLE: + startIdlePhase(); + break; + case PREGAME: + startPregamePhase(); + break; + case COUNTDOWN: + startCountdownPhase(); + break; + case GAME: + startGamePhase(); + break; + case POSTGAME: + startPostgamePhase(); + break; + default: + logger.debug("Attempted to transition into invalid phase " + to.toString()); + } + }; + + private void endIdlePhase() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + if (gameEntity.hasComponent(IdlePhaseComponent.class)) { + gameEntity.removeComponent(IdlePhaseComponent.class); + } else { + logger.debug("Ending idle phase without `IdlePhaseComponent` being present."); + } + gameEntity.send(new OnIdlePhaseEndedEvent()); + }; + + private void endPregamePhase() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + if (gameEntity.hasComponent(PregamePhaseComponent.class)) { + gameEntity.removeComponent(PregamePhaseComponent.class); + } else { + logger.debug("Ending pregame phase without `PregamePhaseComponent` being present."); + } + gameEntity.send(new OnPregamePhaseEndedEvent()); + }; + + private void endCountdownPhase() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + if (gameEntity.hasComponent(CountdownPhaseComponent.class)) { + gameEntity.removeComponent(CountdownPhaseComponent.class); + } else { + logger.debug("Ending countdown phase without `CountdownPhaseComponent` being present."); + } + gameEntity.send(new OnCountdownPhaseEndedEvent()); + }; + + private void endGamePhase() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + if (gameEntity.hasComponent(GamePhaseComponent.class)) { + gameEntity.removeComponent(GamePhaseComponent.class); + } else { + logger.debug("Ending game phase without `GamePhaseComponent` being present."); + } + gameEntity.send(new OnGamePhaseEndedEvent()); + }; + + private void endPostgamePhase() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + if (gameEntity.hasComponent(PostgamePhaseComponent.class)) { + gameEntity.removeComponent(PostgamePhaseComponent.class); + } else { + logger.debug("Ending postgame phase without `PostgamePhaseComponent` being present."); + } + gameEntity.send(new OnPostgamePhaseEndedEvent()); + }; + + private void startIdlePhase() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + gameEntity.addOrSaveComponent(new IdlePhaseComponent()); + gameEntity.send(new OnIdlePhaseStartedEvent()); + }; + + private void startPregamePhase() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + gameEntity.addOrSaveComponent(new PregamePhaseComponent()); + gameEntity.send(new OnPregamePhaseStartedEvent()); + }; + + private void startCountdownPhase() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + gameEntity.addOrSaveComponent(new CountdownPhaseComponent()); + gameEntity.send(new OnCountdownPhaseStartedEvent()); + }; + + private void startGamePhase() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + gameEntity.addOrSaveComponent(new GamePhaseComponent()); + gameEntity.send(new OnGamePhaseStartedEvent()); + }; + + private void startPostgamePhase() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + gameEntity.addOrSaveComponent(new PostgamePhaseComponent()); + gameEntity.send(new OnPostgamePhaseStartedEvent()); + }; +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/PostgamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/PostgamePhaseComponent.java new file mode 100644 index 00000000..ecddeed9 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/PostgamePhaseComponent.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +public class PostgamePhaseComponent implements PhaseComponent { + + public final PhaseSystem.Phase toPhase() { + return PhaseSystem.Phase.POSTGAME; + } + + @Override + public void copyFrom(PhaseComponent other) { + + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/PregamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/statemachine/PregamePhaseComponent.java new file mode 100644 index 00000000..5a24eee7 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/statemachine/PregamePhaseComponent.java @@ -0,0 +1,16 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +public class PregamePhaseComponent implements PhaseComponent { + + public final PhaseSystem.Phase toPhase() { + return PhaseSystem.Phase.PREGAME; + } + + @Override + public void copyFrom(PhaseComponent other) { + + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/PhaseSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/PhaseSystem.java deleted file mode 100644 index fd33a6e3..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/systems/PhaseSystem.java +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.systems; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.terasology.engine.entitySystem.entity.EntityManager; -import org.terasology.engine.entitySystem.entity.EntityRef; -import org.terasology.engine.entitySystem.systems.BaseComponentSystem; -import org.terasology.engine.entitySystem.systems.RegisterSystem; -import org.terasology.engine.registry.In; -import org.terasology.engine.registry.Share; -import org.terasology.module.lightandshadow.components.CountdownPhaseComponent; -import org.terasology.module.lightandshadow.components.GamePhaseComponent; -import org.terasology.module.lightandshadow.components.IdlePhaseComponent; -import org.terasology.module.lightandshadow.components.PostgamePhaseComponent; -import org.terasology.module.lightandshadow.components.PregamePhaseComponent; - -/** - * Provides an entity that keeps track of game state information. - */ -@RegisterSystem -@Share(value = PhaseSystem.class) -public class PhaseSystem extends BaseComponentSystem { - - private static final Logger logger = LoggerFactory.getLogger(PhaseSystem.class); - - @In - EntityManager entityManager; - - @In - private GameEntitySystem gameEntitySystem; - - @Override - public void initialise() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.addOrSaveComponent(new IdlePhaseComponent()); - } - - // TODO: Would it make more sense to ask for the current phase instead or even in addition? - - public boolean isInIdlePhase() { - return gameEntitySystem.getGameEntity().hasComponent(IdlePhaseComponent.class); - } - - public boolean isInPregamePhase() { - return gameEntitySystem.getGameEntity().hasComponent(PregamePhaseComponent.class); - } - - public boolean isInCountdownPhase() { - return gameEntitySystem.getGameEntity().hasComponent(CountdownPhaseComponent.class); - } - - public boolean isInGamePhase() { - return gameEntitySystem.getGameEntity().hasComponent(GamePhaseComponent.class); - } - - public boolean isInPostgamePhase() { - return gameEntitySystem.getGameEntity().hasComponent(PostgamePhaseComponent.class); - } -} diff --git a/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystemTest.java new file mode 100644 index 00000000..e3ab308a --- /dev/null +++ b/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystemTest.java @@ -0,0 +1,44 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.entity.internal.PojoEntityManager; +import org.terasology.module.lightandshadow.systems.GameEntitySystem; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class PhaseCommandsSystemTest { + PhaseCommandsSystem phaseCommandsSystem; + + @BeforeEach + void setUp() { + phaseCommandsSystem = new PhaseCommandsSystem(); + EntityRef gameEntity = new PojoEntityManager().create(); + IdlePhaseComponent idlePhaseComponent = new IdlePhaseComponent(); + gameEntity.saveComponent(idlePhaseComponent); + createMockPhaseSystem(gameEntity); + } + + private void createMockPhaseSystem(EntityRef gameEntity) { + phaseCommandsSystem.phaseSystem = mock(PhaseSystem.class); + when(phaseCommandsSystem.phaseSystem.gameEntitySystem.getGameEntity()).thenReturn(gameEntity); + } + + @ParameterizedTest + @EnumSource(PhaseSystem.Phase.class) + void testForceIdlePhaseSuccessful(PhaseSystem.Phase currentPhase) { + phaseCommandsSystem.forcePhase(PhaseSystem.Phase.IDLE); + verify(phaseCommandsSystem.phaseSystem, times(1)).transitionPhase(currentPhase, PhaseSystem.Phase.IDLE); + } + +} \ No newline at end of file diff --git a/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java new file mode 100644 index 00000000..7e9df0b3 --- /dev/null +++ b/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java @@ -0,0 +1,41 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.statemachine; + +import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.entity.internal.PojoEntityManager; +import org.terasology.module.lightandshadow.systems.GameEntitySystem; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class PhaseSystemTest { + PhaseSystem phaseSystem; + private GameEntitySystem gameEntitySystem; + + @BeforeEach + void setUp() { + phaseSystem = new PhaseSystem(); + EntityRef gameEntity = new PojoEntityManager().create(); + IdlePhaseComponent idlePhaseComponent = new IdlePhaseComponent(); + gameEntity.saveComponent(idlePhaseComponent); + createMockGameEntitySystem(gameEntity); + } + + private void createMockGameEntitySystem(EntityRef gameEntity) { + phaseSystem.gameEntitySystem = mock(GameEntitySystem.class); + when(phaseSystem.gameEntitySystem.getGameEntity()).thenReturn(gameEntity); + } + + @ParameterizedTest + @EnumSource(PhaseSystem.Phase.class) + void transitionToIdlePhase(PhaseSystem.Phase from) { + + } +} \ No newline at end of file From bc81faebac1c780f7ede1983ef1b0e1de42ef180 Mon Sep 17 00:00:00 2001 From: Tobias Nett Date: Fri, 17 Dec 2021 21:21:09 +0100 Subject: [PATCH 05/51] rename `statemachine` package to `phases` --- .../{statemachine => phases}/CountdownPhaseComponent.java | 2 +- .../{statemachine => phases}/GamePhaseComponent.java | 2 +- .../{statemachine => phases}/IdlePhaseComponent.java | 2 +- .../OnCountdownPhaseEndedEvent.java | 2 +- .../OnCountdownPhaseStartedEvent.java | 2 +- .../{statemachine => phases}/OnGamePhaseEndedEvent.java | 2 +- .../{statemachine => phases}/OnGamePhaseStartedEvent.java | 2 +- .../{statemachine => phases}/OnIdlePhaseEndedEvent.java | 2 +- .../{statemachine => phases}/OnIdlePhaseStartedEvent.java | 2 +- .../{statemachine => phases}/OnPostgamePhaseEndedEvent.java | 2 +- .../OnPostgamePhaseStartedEvent.java | 2 +- .../{statemachine => phases}/OnPregamePhaseEndedEvent.java | 2 +- .../OnPregamePhaseStartedEvent.java | 2 +- .../{statemachine => phases}/PhaseCommandsSystem.java | 2 +- .../{statemachine => phases}/PhaseComponent.java | 2 +- .../{statemachine => phases}/PhaseSystem.java | 3 +-- .../{statemachine => phases}/PostgamePhaseComponent.java | 2 +- .../{statemachine => phases}/PregamePhaseComponent.java | 2 +- .../statemachine/PhaseCommandsSystemTest.java | 6 +++--- .../module/lightandshadow/statemachine/PhaseSystemTest.java | 4 ++-- 20 files changed, 23 insertions(+), 24 deletions(-) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/CountdownPhaseComponent.java (84%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/GamePhaseComponent.java (84%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/IdlePhaseComponent.java (84%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/OnCountdownPhaseEndedEvent.java (87%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/OnCountdownPhaseStartedEvent.java (87%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/OnGamePhaseEndedEvent.java (87%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/OnGamePhaseStartedEvent.java (87%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/OnIdlePhaseEndedEvent.java (87%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/OnIdlePhaseStartedEvent.java (87%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/OnPostgamePhaseEndedEvent.java (87%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/OnPostgamePhaseStartedEvent.java (87%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/OnPregamePhaseEndedEvent.java (87%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/OnPregamePhaseStartedEvent.java (87%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/PhaseCommandsSystem.java (97%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/PhaseComponent.java (81%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/PhaseSystem.java (98%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/PostgamePhaseComponent.java (84%) rename src/main/java/org/terasology/module/lightandshadow/{statemachine => phases}/PregamePhaseComponent.java (84%) diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/CountdownPhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/CountdownPhaseComponent.java similarity index 84% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/CountdownPhaseComponent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/CountdownPhaseComponent.java index 7b1515ec..cc7cfa77 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/CountdownPhaseComponent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/CountdownPhaseComponent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; public class CountdownPhaseComponent implements PhaseComponent { diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/GamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/GamePhaseComponent.java similarity index 84% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/GamePhaseComponent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/GamePhaseComponent.java index dac0ca15..315e608a 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/GamePhaseComponent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/GamePhaseComponent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; public class GamePhaseComponent implements PhaseComponent { diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/IdlePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/IdlePhaseComponent.java similarity index 84% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/IdlePhaseComponent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/IdlePhaseComponent.java index faf8f54e..dcc44c9d 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/IdlePhaseComponent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/IdlePhaseComponent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; public class IdlePhaseComponent implements PhaseComponent { diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseEndedEvent.java similarity index 87% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseEndedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseEndedEvent.java index ca376830..7c760237 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseEndedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseEndedEvent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.terasology.engine.entitySystem.event.Event; import org.terasology.engine.network.BroadcastEvent; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseStartedEvent.java similarity index 87% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseStartedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseStartedEvent.java index a96d7092..befa24fa 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnCountdownPhaseStartedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseStartedEvent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; import org.terasology.engine.entitySystem.event.Event; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseEndedEvent.java similarity index 87% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseEndedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseEndedEvent.java index 26ef85e9..204054d1 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseEndedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseEndedEvent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; import org.terasology.engine.entitySystem.event.Event; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseStartedEvent.java similarity index 87% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseStartedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseStartedEvent.java index e9ab7184..dbaf8bd2 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnGamePhaseStartedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseStartedEvent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; import org.terasology.engine.entitySystem.event.Event; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseEndedEvent.java similarity index 87% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseEndedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseEndedEvent.java index f9395c7f..07aaeed9 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseEndedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseEndedEvent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; import org.terasology.engine.entitySystem.event.Event; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseStartedEvent.java similarity index 87% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseStartedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseStartedEvent.java index aaf170d5..f1c7426f 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnIdlePhaseStartedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseStartedEvent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; import org.terasology.engine.entitySystem.event.Event; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseEndedEvent.java similarity index 87% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseEndedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseEndedEvent.java index de56a1d7..8a5808a7 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseEndedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseEndedEvent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; import org.terasology.engine.entitySystem.event.Event; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseStartedEvent.java similarity index 87% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseStartedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseStartedEvent.java index cd075ade..ff72bf6f 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPostgamePhaseStartedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseStartedEvent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; import org.terasology.engine.entitySystem.event.Event; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseEndedEvent.java similarity index 87% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseEndedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseEndedEvent.java index 23d1d918..a023146a 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseEndedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseEndedEvent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.terasology.engine.entitySystem.event.Event; import org.terasology.engine.network.BroadcastEvent; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseStartedEvent.java similarity index 87% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseStartedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseStartedEvent.java index f7dddce2..a9195f3d 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/OnPregamePhaseStartedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseStartedEvent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; import org.terasology.engine.entitySystem.event.Event; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystem.java similarity index 97% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystem.java rename to src/main/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystem.java index e0c99f54..15817923 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystem.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseComponent.java similarity index 81% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseComponent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/PhaseComponent.java index ca3bb5ff..42afaac2 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseComponent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseComponent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.terasology.gestalt.entitysystem.component.Component; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java similarity index 98% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseSystem.java rename to src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java index 57981a24..9d05364a 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/PhaseSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java @@ -1,11 +1,10 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.terasology.engine.entitySystem.entity.EntityManager; import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterSystem; diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/PostgamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/PostgamePhaseComponent.java similarity index 84% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/PostgamePhaseComponent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/PostgamePhaseComponent.java index ecddeed9..cfdd1817 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/PostgamePhaseComponent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/PostgamePhaseComponent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; public class PostgamePhaseComponent implements PhaseComponent { diff --git a/src/main/java/org/terasology/module/lightandshadow/statemachine/PregamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/PregamePhaseComponent.java similarity index 84% rename from src/main/java/org/terasology/module/lightandshadow/statemachine/PregamePhaseComponent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/PregamePhaseComponent.java index 5a24eee7..67112260 100644 --- a/src/main/java/org/terasology/module/lightandshadow/statemachine/PregamePhaseComponent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/PregamePhaseComponent.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; public class PregamePhaseComponent implements PhaseComponent { diff --git a/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystemTest.java index e3ab308a..719072ec 100644 --- a/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystemTest.java +++ b/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystemTest.java @@ -3,15 +3,15 @@ package org.terasology.module.lightandshadow.statemachine; -import org.junit.Test; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.entity.internal.PojoEntityManager; -import org.terasology.module.lightandshadow.systems.GameEntitySystem; +import org.terasology.module.lightandshadow.phases.IdlePhaseComponent; +import org.terasology.module.lightandshadow.phases.PhaseCommandsSystem; +import org.terasology.module.lightandshadow.phases.PhaseSystem; -import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; diff --git a/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java index 7e9df0b3..c85593d9 100644 --- a/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java +++ b/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java @@ -3,13 +3,13 @@ package org.terasology.module.lightandshadow.statemachine; -import org.junit.Test; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.junit.jupiter.params.provider.ValueSource; import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.entity.internal.PojoEntityManager; +import org.terasology.module.lightandshadow.phases.IdlePhaseComponent; +import org.terasology.module.lightandshadow.phases.PhaseSystem; import org.terasology.module.lightandshadow.systems.GameEntitySystem; import static org.mockito.Mockito.mock; From 82c2a48677e80b81b5af046e9a4970d59110e6c8 Mon Sep 17 00:00:00 2001 From: Tobias Nett Date: Fri, 17 Dec 2021 21:57:08 +0100 Subject: [PATCH 06/51] get all the things up to speed again - move `Phase` enum out of `PhaseSystem` - simplify `Phase` enum (for now) - remove separate components for phases - fix `start/end..Phase` methods --- .../phases/CountdownPhaseComponent.java | 16 -- .../phases/GamePhaseComponent.java | 16 -- .../phases/IdlePhaseComponent.java | 16 -- .../phases/OnCountdownPhaseEndedEvent.java | 2 +- .../phases/OnCountdownPhaseStartedEvent.java | 2 +- .../phases/OnGamePhaseStartedEvent.java | 2 +- .../phases/OnIdlePhaseEndedEvent.java | 2 +- .../phases/OnIdlePhaseStartedEvent.java | 2 +- ...vent.java => OnInGamePhaseEndedEvent.java} | 6 +- ...nt.java => OnPostGamePhaseEndedEvent.java} | 6 +- ....java => OnPostGamePhaseStartedEvent.java} | 6 +- ...ent.java => OnPreGamePhaseEndedEvent.java} | 6 +- ...t.java => OnPreGamePhaseStartedEvent.java} | 6 +- .../module/lightandshadow/phases/Phase.java | 12 ++ .../phases/PhaseCommandsSystem.java | 14 +- .../lightandshadow/phases/PhaseComponent.java | 28 ++- .../lightandshadow/phases/PhaseSystem.java | 163 +++++++----------- .../phases/PostgamePhaseComponent.java | 16 -- .../phases/PregamePhaseComponent.java | 16 -- .../PhaseCommandsSystemTest.java | 16 +- .../statemachine/PhaseSystemTest.java | 41 ----- 21 files changed, 132 insertions(+), 262 deletions(-) delete mode 100644 src/main/java/org/terasology/module/lightandshadow/phases/CountdownPhaseComponent.java delete mode 100644 src/main/java/org/terasology/module/lightandshadow/phases/GamePhaseComponent.java delete mode 100644 src/main/java/org/terasology/module/lightandshadow/phases/IdlePhaseComponent.java rename src/main/java/org/terasology/module/lightandshadow/phases/{OnGamePhaseEndedEvent.java => OnInGamePhaseEndedEvent.java} (67%) rename src/main/java/org/terasology/module/lightandshadow/phases/{OnPostgamePhaseEndedEvent.java => OnPostGamePhaseEndedEvent.java} (67%) rename src/main/java/org/terasology/module/lightandshadow/phases/{OnPostgamePhaseStartedEvent.java => OnPostGamePhaseStartedEvent.java} (66%) rename src/main/java/org/terasology/module/lightandshadow/phases/{OnPregamePhaseEndedEvent.java => OnPreGamePhaseEndedEvent.java} (67%) rename src/main/java/org/terasology/module/lightandshadow/phases/{OnPregamePhaseStartedEvent.java => OnPreGamePhaseStartedEvent.java} (66%) create mode 100644 src/main/java/org/terasology/module/lightandshadow/phases/Phase.java delete mode 100644 src/main/java/org/terasology/module/lightandshadow/phases/PostgamePhaseComponent.java delete mode 100644 src/main/java/org/terasology/module/lightandshadow/phases/PregamePhaseComponent.java rename src/test/java/org/terasology/module/lightandshadow/{statemachine => phases}/PhaseCommandsSystemTest.java (65%) delete mode 100644 src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/CountdownPhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/CountdownPhaseComponent.java deleted file mode 100644 index cc7cfa77..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/phases/CountdownPhaseComponent.java +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.phases; - -public class CountdownPhaseComponent implements PhaseComponent { - - public final PhaseSystem.Phase toPhase() { - return PhaseSystem.Phase.COUNTDOWN; - } - - @Override - public void copyFrom(PhaseComponent other) { - - } -} diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/GamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/GamePhaseComponent.java deleted file mode 100644 index 315e608a..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/phases/GamePhaseComponent.java +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.phases; - -public class GamePhaseComponent implements PhaseComponent { - - public final PhaseSystem.Phase toPhase() { - return PhaseSystem.Phase.GAME; - } - - @Override - public void copyFrom(PhaseComponent other) { - - } -} diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/IdlePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/IdlePhaseComponent.java deleted file mode 100644 index dcc44c9d..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/phases/IdlePhaseComponent.java +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.phases; - -public class IdlePhaseComponent implements PhaseComponent { - - public final PhaseSystem.Phase toPhase() { - return PhaseSystem.Phase.IDLE; - } - - @Override - public void copyFrom(PhaseComponent other) { - - } -} diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseEndedEvent.java index 7c760237..88b0fc7f 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseEndedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseEndedEvent.java @@ -3,8 +3,8 @@ package org.terasology.module.lightandshadow.phases; -import org.terasology.engine.entitySystem.event.Event; import org.terasology.engine.network.BroadcastEvent; +import org.terasology.gestalt.entitysystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseStartedEvent.java index befa24fa..4cab7196 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseStartedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnCountdownPhaseStartedEvent.java @@ -4,7 +4,7 @@ package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.engine.entitySystem.event.Event; +import org.terasology.gestalt.entitysystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseStartedEvent.java index dbaf8bd2..badebf19 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseStartedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseStartedEvent.java @@ -4,7 +4,7 @@ package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.engine.entitySystem.event.Event; +import org.terasology.gestalt.entitysystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseEndedEvent.java index 07aaeed9..e1ff344e 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseEndedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseEndedEvent.java @@ -4,7 +4,7 @@ package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.engine.entitySystem.event.Event; +import org.terasology.gestalt.entitysystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseStartedEvent.java index f1c7426f..e847a0e0 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseStartedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnIdlePhaseStartedEvent.java @@ -4,7 +4,7 @@ package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.engine.entitySystem.event.Event; +import org.terasology.gestalt.entitysystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnInGamePhaseEndedEvent.java similarity index 67% rename from src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseEndedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnInGamePhaseEndedEvent.java index 204054d1..dff174cf 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseEndedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnInGamePhaseEndedEvent.java @@ -4,13 +4,13 @@ package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.engine.entitySystem.event.Event; +import org.terasology.gestalt.entitysystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? @BroadcastEvent -public class OnGamePhaseEndedEvent implements Event { +public class OnInGamePhaseEndedEvent implements Event { - public OnGamePhaseEndedEvent() { + public OnInGamePhaseEndedEvent() { } } diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnPostGamePhaseEndedEvent.java similarity index 67% rename from src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseEndedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnPostGamePhaseEndedEvent.java index 8a5808a7..94f31caa 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseEndedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnPostGamePhaseEndedEvent.java @@ -4,13 +4,13 @@ package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.engine.entitySystem.event.Event; +import org.terasology.gestalt.entitysystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? @BroadcastEvent -public class OnPostgamePhaseEndedEvent implements Event { +public class OnPostGamePhaseEndedEvent implements Event { - public OnPostgamePhaseEndedEvent() { + public OnPostGamePhaseEndedEvent() { } } diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnPostGamePhaseStartedEvent.java similarity index 66% rename from src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseStartedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnPostGamePhaseStartedEvent.java index ff72bf6f..e060411a 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/OnPostgamePhaseStartedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnPostGamePhaseStartedEvent.java @@ -4,13 +4,13 @@ package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.engine.entitySystem.event.Event; +import org.terasology.gestalt.entitysystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? @BroadcastEvent -public class OnPostgamePhaseStartedEvent implements Event { +public class OnPostGamePhaseStartedEvent implements Event { - public OnPostgamePhaseStartedEvent() { + public OnPostGamePhaseStartedEvent() { } } diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseEndedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnPreGamePhaseEndedEvent.java similarity index 67% rename from src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseEndedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnPreGamePhaseEndedEvent.java index a023146a..c0bca413 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseEndedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnPreGamePhaseEndedEvent.java @@ -3,14 +3,14 @@ package org.terasology.module.lightandshadow.phases; -import org.terasology.engine.entitySystem.event.Event; import org.terasology.engine.network.BroadcastEvent; +import org.terasology.gestalt.entitysystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? @BroadcastEvent -public class OnPregamePhaseEndedEvent implements Event { +public class OnPreGamePhaseEndedEvent implements Event { - public OnPregamePhaseEndedEvent() { + public OnPreGamePhaseEndedEvent() { } } diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnPreGamePhaseStartedEvent.java similarity index 66% rename from src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseStartedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnPreGamePhaseStartedEvent.java index a9195f3d..4c939c02 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/OnPregamePhaseStartedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnPreGamePhaseStartedEvent.java @@ -4,13 +4,13 @@ package org.terasology.module.lightandshadow.phases; import org.terasology.engine.network.BroadcastEvent; -import org.terasology.engine.entitySystem.event.Event; +import org.terasology.gestalt.entitysystem.event.Event; // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? @BroadcastEvent -public class OnPregamePhaseStartedEvent implements Event { +public class OnPreGamePhaseStartedEvent implements Event { - public OnPregamePhaseStartedEvent() { + public OnPreGamePhaseStartedEvent() { } } diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/Phase.java b/src/main/java/org/terasology/module/lightandshadow/phases/Phase.java new file mode 100644 index 00000000..053d45f0 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/phases/Phase.java @@ -0,0 +1,12 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.phases; + +enum Phase { + IDLE, + PRE_GAME, + COUNTDOWN, + IN_GAME, + POST_GAME; +} diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystem.java index 15817923..299fa328 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystem.java @@ -27,25 +27,25 @@ public class PhaseCommandsSystem extends BaseComponentSystem { requiredPermission = PermissionManager.CHEAT_PERMISSION) public String forcePhase( @Sender EntityRef client, - @CommandParam("targetPhase") PhaseSystem.Phase targetPhase) { + @CommandParam("targetPhase") Phase targetPhase) { - switch(targetPhase) { + switch (targetPhase) { case IDLE: - case PREGAME: + case PRE_GAME: case COUNTDOWN: - case GAME: - case POSTGAME: + case IN_GAME: + case POST_GAME: break; default: StringBuilder errorMsg = new StringBuilder(targetPhase + " is not a valid phase, please choose one of the following:"); - for (PhaseSystem.Phase validPhase : PhaseSystem.Phase.values()) { + for (Phase validPhase : Phase.values()) { errorMsg.append(" ").append(validPhase.toString()); } logger.error(errorMsg.toString()); return errorMsg.toString(); } - PhaseSystem.Phase currentPhase = phaseSystem.getCurrentPhase(); + Phase currentPhase = phaseSystem.getCurrentPhase(); phaseSystem.transitionPhase(currentPhase, targetPhase); return "Initiated phase transition from " + currentPhase + " to " + targetPhase; } diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseComponent.java index 42afaac2..697a95d0 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseComponent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseComponent.java @@ -5,7 +5,31 @@ import org.terasology.gestalt.entitysystem.component.Component; -public interface PhaseComponent extends Component { +/** + * Read-only component holding the current state of a Light and Shadow game. + */ +public class PhaseComponent implements Component { - public PhaseSystem.Phase toPhase(); + private Phase currentPhase; + + PhaseComponent() { + this.currentPhase = Phase.IDLE; + } + + public Phase getCurrentPhase() { + return currentPhase; + } + + /** + * Package-private setter to only allow the authority system to update the game state. + * @param newPhase the new active game phase + */ + void setCurrentPhase(Phase newPhase) { + this.currentPhase = newPhase; + } + + @Override + public void copyFrom(PhaseComponent other) { + this.currentPhase = other.currentPhase; + } } diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java index 9d05364a..b7c9c803 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java @@ -10,7 +10,6 @@ import org.terasology.engine.entitySystem.systems.RegisterSystem; import org.terasology.engine.registry.In; import org.terasology.engine.registry.Share; -import org.terasology.gestalt.naming.Name; import org.terasology.module.lightandshadow.systems.GameEntitySystem; /** @@ -25,61 +24,15 @@ public class PhaseSystem extends BaseComponentSystem { @In GameEntitySystem gameEntitySystem; - enum Phase { - IDLE(new Name("Idle")), - PREGAME(new Name("Pregame")), - COUNTDOWN(new Name("Countdown")), - GAME(new Name("Game")), - POSTGAME(new Name("Postgame")); - - private final Name phaseName; - - Phase(final Name phaseName) { - this.phaseName = phaseName; - } - - @Override - public final String toString() { - return phaseName.toString(); - } - - public Phase fromString(String phaseString) { - Name input = new Name(phaseString); - switch (input.toString()) { - case IDLE.phaseName.toString(): - } - } - } - @Override public void initialise() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.addOrSaveComponent(new IdlePhaseComponent()); + gameEntity.addOrSaveComponent(new PhaseComponent()); } // TODO: What happens here if there's multiple components present that implement PhaseComponent? public Phase getCurrentPhase() { - return gameEntitySystem.getGameEntity().getComponent(PhaseComponent.class).toPhase(); - } - - public boolean isInIdlePhase() { - return gameEntitySystem.getGameEntity().hasComponent(IdlePhaseComponent.class); - } - - public boolean isInPregamePhase() { - return gameEntitySystem.getGameEntity().hasComponent(PregamePhaseComponent.class); - } - - public boolean isInCountdownPhase() { - return gameEntitySystem.getGameEntity().hasComponent(CountdownPhaseComponent.class); - } - - public boolean isInGamePhase() { - return gameEntitySystem.getGameEntity().hasComponent(GamePhaseComponent.class); - } - - public boolean isInPostgamePhase() { - return gameEntitySystem.getGameEntity().hasComponent(PostgamePhaseComponent.class); + return gameEntitySystem.getGameEntity().getComponent(PhaseComponent.class).getCurrentPhase(); } void transitionPhase(Phase from, Phase to) { @@ -88,17 +41,17 @@ void transitionPhase(Phase from, Phase to) { case IDLE: endIdlePhase(); break; - case PREGAME: - endPregamePhase(); + case PRE_GAME: + endPreGamePhase(); break; case COUNTDOWN: endCountdownPhase(); break; - case GAME: - endGamePhase(); + case IN_GAME: + endInGamePhase(); break; - case POSTGAME: - endPostgamePhase(); + case POST_GAME: + endPostGamePhase(); break; default: logger.debug("Attempted to transition from invalid phase " + to.toString()); @@ -108,100 +61,106 @@ void transitionPhase(Phase from, Phase to) { case IDLE: startIdlePhase(); break; - case PREGAME: - startPregamePhase(); + case PRE_GAME: + startPreGamePhase(); break; case COUNTDOWN: startCountdownPhase(); break; - case GAME: - startGamePhase(); + case IN_GAME: + startInGamePhase(); break; - case POSTGAME: - startPostgamePhase(); + case POST_GAME: + startPostGamePhase(); break; default: logger.debug("Attempted to transition into invalid phase " + to.toString()); } }; + private void assertInPhase(Phase expectedPhase, EntityRef gameEntity) { + PhaseComponent phase = gameEntity.getComponent(PhaseComponent.class); + + if (phase == null || phase.getCurrentPhase() != expectedPhase) { + String message = "LightAndShadow/PhaseSystem: Attempting to end idle phase without being in `" + expectedPhase + "` phase."; + String entityDump = gameEntity.toFullDescription(); + throw new RuntimeException(message + "\n" + entityDump); + } + gameEntity.send(new OnIdlePhaseEndedEvent()); + } + private void endIdlePhase() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - if (gameEntity.hasComponent(IdlePhaseComponent.class)) { - gameEntity.removeComponent(IdlePhaseComponent.class); - } else { - logger.debug("Ending idle phase without `IdlePhaseComponent` being present."); - } + assertInPhase(Phase.IDLE, gameEntity); gameEntity.send(new OnIdlePhaseEndedEvent()); }; - private void endPregamePhase() { + private void endPreGamePhase() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - if (gameEntity.hasComponent(PregamePhaseComponent.class)) { - gameEntity.removeComponent(PregamePhaseComponent.class); - } else { - logger.debug("Ending pregame phase without `PregamePhaseComponent` being present."); - } - gameEntity.send(new OnPregamePhaseEndedEvent()); + assertInPhase(Phase.PRE_GAME, gameEntity); + gameEntity.send(new OnPreGamePhaseEndedEvent()); }; private void endCountdownPhase() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - if (gameEntity.hasComponent(CountdownPhaseComponent.class)) { - gameEntity.removeComponent(CountdownPhaseComponent.class); - } else { - logger.debug("Ending countdown phase without `CountdownPhaseComponent` being present."); - } + assertInPhase(Phase.COUNTDOWN, gameEntity); gameEntity.send(new OnCountdownPhaseEndedEvent()); }; - private void endGamePhase() { + private void endInGamePhase() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - if (gameEntity.hasComponent(GamePhaseComponent.class)) { - gameEntity.removeComponent(GamePhaseComponent.class); - } else { - logger.debug("Ending game phase without `GamePhaseComponent` being present."); - } - gameEntity.send(new OnGamePhaseEndedEvent()); + assertInPhase(Phase.IN_GAME, gameEntity); + gameEntity.send(new OnInGamePhaseEndedEvent()); }; - private void endPostgamePhase() { + private void endPostGamePhase() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - if (gameEntity.hasComponent(PostgamePhaseComponent.class)) { - gameEntity.removeComponent(PostgamePhaseComponent.class); - } else { - logger.debug("Ending postgame phase without `PostgamePhaseComponent` being present."); - } - gameEntity.send(new OnPostgamePhaseEndedEvent()); + assertInPhase(Phase.POST_GAME, gameEntity); + gameEntity.send(new OnPostGamePhaseEndedEvent()); }; private void startIdlePhase() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.addOrSaveComponent(new IdlePhaseComponent()); + gameEntity.updateComponent(PhaseComponent.class, phaseComponent -> { + phaseComponent.setCurrentPhase(Phase.IDLE); + return phaseComponent; + }); gameEntity.send(new OnIdlePhaseStartedEvent()); }; - private void startPregamePhase() { + private void startPreGamePhase() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.addOrSaveComponent(new PregamePhaseComponent()); - gameEntity.send(new OnPregamePhaseStartedEvent()); + gameEntity.updateComponent(PhaseComponent.class, phaseComponent -> { + phaseComponent.setCurrentPhase(Phase.PRE_GAME); + return phaseComponent; + }); + gameEntity.send(new OnPreGamePhaseStartedEvent()); }; private void startCountdownPhase() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.addOrSaveComponent(new CountdownPhaseComponent()); + gameEntity.updateComponent(PhaseComponent.class, phaseComponent -> { + phaseComponent.setCurrentPhase(Phase.COUNTDOWN); + return phaseComponent; + }); gameEntity.send(new OnCountdownPhaseStartedEvent()); }; - private void startGamePhase() { + private void startInGamePhase() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.addOrSaveComponent(new GamePhaseComponent()); + gameEntity.updateComponent(PhaseComponent.class, phaseComponent -> { + phaseComponent.setCurrentPhase(Phase.IN_GAME); + return phaseComponent; + }); gameEntity.send(new OnGamePhaseStartedEvent()); }; - private void startPostgamePhase() { + private void startPostGamePhase() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.addOrSaveComponent(new PostgamePhaseComponent()); - gameEntity.send(new OnPostgamePhaseStartedEvent()); + gameEntity.updateComponent(PhaseComponent.class, phaseComponent -> { + phaseComponent.setCurrentPhase(Phase.POST_GAME); + return phaseComponent; + }); + gameEntity.send(new OnPostGamePhaseStartedEvent()); }; } diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/PostgamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/PostgamePhaseComponent.java deleted file mode 100644 index cfdd1817..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/phases/PostgamePhaseComponent.java +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.phases; - -public class PostgamePhaseComponent implements PhaseComponent { - - public final PhaseSystem.Phase toPhase() { - return PhaseSystem.Phase.POSTGAME; - } - - @Override - public void copyFrom(PhaseComponent other) { - - } -} diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/PregamePhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/PregamePhaseComponent.java deleted file mode 100644 index 67112260..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/phases/PregamePhaseComponent.java +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.phases; - -public class PregamePhaseComponent implements PhaseComponent { - - public final PhaseSystem.Phase toPhase() { - return PhaseSystem.Phase.PREGAME; - } - - @Override - public void copyFrom(PhaseComponent other) { - - } -} diff --git a/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystemTest.java similarity index 65% rename from src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystemTest.java rename to src/test/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystemTest.java index 719072ec..6dad8d03 100644 --- a/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseCommandsSystemTest.java +++ b/src/test/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystemTest.java @@ -1,16 +1,13 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.statemachine; +package org.terasology.module.lightandshadow.phases; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.entity.internal.PojoEntityManager; -import org.terasology.module.lightandshadow.phases.IdlePhaseComponent; -import org.terasology.module.lightandshadow.phases.PhaseCommandsSystem; -import org.terasology.module.lightandshadow.phases.PhaseSystem; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -24,8 +21,7 @@ class PhaseCommandsSystemTest { void setUp() { phaseCommandsSystem = new PhaseCommandsSystem(); EntityRef gameEntity = new PojoEntityManager().create(); - IdlePhaseComponent idlePhaseComponent = new IdlePhaseComponent(); - gameEntity.saveComponent(idlePhaseComponent); + gameEntity.saveComponent(new PhaseComponent()); createMockPhaseSystem(gameEntity); } @@ -35,10 +31,10 @@ private void createMockPhaseSystem(EntityRef gameEntity) { } @ParameterizedTest - @EnumSource(PhaseSystem.Phase.class) - void testForceIdlePhaseSuccessful(PhaseSystem.Phase currentPhase) { - phaseCommandsSystem.forcePhase(PhaseSystem.Phase.IDLE); - verify(phaseCommandsSystem.phaseSystem, times(1)).transitionPhase(currentPhase, PhaseSystem.Phase.IDLE); + @EnumSource(Phase.class) + void testForceIdlePhaseSuccessful(Phase currentPhase) { + phaseCommandsSystem.forcePhase(EntityRef.NULL, Phase.IDLE); // TODO: use non-NULL entity + verify(phaseCommandsSystem.phaseSystem, times(1)).transitionPhase(currentPhase, Phase.IDLE); } } \ No newline at end of file diff --git a/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java deleted file mode 100644 index c85593d9..00000000 --- a/src/test/java/org/terasology/module/lightandshadow/statemachine/PhaseSystemTest.java +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.statemachine; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; -import org.terasology.engine.entitySystem.entity.EntityRef; -import org.terasology.engine.entitySystem.entity.internal.PojoEntityManager; -import org.terasology.module.lightandshadow.phases.IdlePhaseComponent; -import org.terasology.module.lightandshadow.phases.PhaseSystem; -import org.terasology.module.lightandshadow.systems.GameEntitySystem; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -class PhaseSystemTest { - PhaseSystem phaseSystem; - private GameEntitySystem gameEntitySystem; - - @BeforeEach - void setUp() { - phaseSystem = new PhaseSystem(); - EntityRef gameEntity = new PojoEntityManager().create(); - IdlePhaseComponent idlePhaseComponent = new IdlePhaseComponent(); - gameEntity.saveComponent(idlePhaseComponent); - createMockGameEntitySystem(gameEntity); - } - - private void createMockGameEntitySystem(EntityRef gameEntity) { - phaseSystem.gameEntitySystem = mock(GameEntitySystem.class); - when(phaseSystem.gameEntitySystem.getGameEntity()).thenReturn(gameEntity); - } - - @ParameterizedTest - @EnumSource(PhaseSystem.Phase.class) - void transitionToIdlePhase(PhaseSystem.Phase from) { - - } -} \ No newline at end of file From e467dffbcade349b787f5a2cc47fc4a4c8a9387d Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Fri, 17 Dec 2021 22:16:49 +0100 Subject: [PATCH 07/51] refactor: fix naming for in-game phase started event --- ...ePhaseStartedEvent.java => OnInGamePhaseStartedEvent.java} | 4 ++-- .../terasology/module/lightandshadow/phases/PhaseSystem.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/main/java/org/terasology/module/lightandshadow/phases/{OnGamePhaseStartedEvent.java => OnInGamePhaseStartedEvent.java} (79%) diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseStartedEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/OnInGamePhaseStartedEvent.java similarity index 79% rename from src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseStartedEvent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/OnInGamePhaseStartedEvent.java index badebf19..5474ad52 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/OnGamePhaseStartedEvent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/OnInGamePhaseStartedEvent.java @@ -9,8 +9,8 @@ // TODO: Would it make more sense to have a single "OnPhaseTransition" with old and new phase here? @BroadcastEvent -public class OnGamePhaseStartedEvent implements Event { +public class OnInGamePhaseStartedEvent implements Event { - public OnGamePhaseStartedEvent() { + public OnInGamePhaseStartedEvent() { } } diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java index b7c9c803..c9ffbba0 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java @@ -152,7 +152,7 @@ private void startInGamePhase() { phaseComponent.setCurrentPhase(Phase.IN_GAME); return phaseComponent; }); - gameEntity.send(new OnGamePhaseStartedEvent()); + gameEntity.send(new OnInGamePhaseStartedEvent()); }; private void startPostGamePhase() { From 6b7eab6e0170e165a65f9b388b55c17f73e7e43a Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Fri, 17 Dec 2021 22:35:58 +0100 Subject: [PATCH 08/51] refactor: simplify phase transitions - create authority package for encapsulation - enhance phase enum with event suppliers - use event suppliers to generalize phase transition in PhaseSystem --- .../module/lightandshadow/phases/Phase.java | 24 ++- .../lightandshadow/phases/PhaseSystem.java | 166 ------------------ .../{ => authority}/PhaseCommandsSystem.java | 3 +- .../{ => authority}/PhaseComponent.java | 3 +- .../phases/authority/PhaseSystem.java | 81 +++++++++ .../PhaseCommandsSystemTest.java | 3 +- 6 files changed, 105 insertions(+), 175 deletions(-) delete mode 100644 src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java rename src/main/java/org/terasology/module/lightandshadow/phases/{ => authority}/PhaseCommandsSystem.java (94%) rename src/main/java/org/terasology/module/lightandshadow/phases/{ => authority}/PhaseComponent.java (87%) create mode 100644 src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java rename src/test/java/org/terasology/module/lightandshadow/phases/{ => authority}/PhaseCommandsSystemTest.java (92%) diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/Phase.java b/src/main/java/org/terasology/module/lightandshadow/phases/Phase.java index 053d45f0..0a981c46 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/Phase.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/Phase.java @@ -3,10 +3,22 @@ package org.terasology.module.lightandshadow.phases; -enum Phase { - IDLE, - PRE_GAME, - COUNTDOWN, - IN_GAME, - POST_GAME; +import org.terasology.gestalt.entitysystem.event.Event; + +import java.util.function.Supplier; + +public enum Phase { + IDLE(OnIdlePhaseStartedEvent::new, OnIdlePhaseEndedEvent::new), + PRE_GAME(OnPreGamePhaseStartedEvent::new, OnPreGamePhaseEndedEvent::new), + COUNTDOWN(OnCountdownPhaseStartedEvent::new, OnCountdownPhaseEndedEvent::new), + IN_GAME(OnInGamePhaseStartedEvent::new, OnInGamePhaseEndedEvent::new), + POST_GAME(OnPostGamePhaseStartedEvent::new, OnPostGamePhaseEndedEvent::new); + + public final Supplier endEvent; + public final Supplier startEvent; + + Phase(Supplier startEvent, Supplier endEvent) { + this.startEvent = startEvent; + this.endEvent = endEvent; + } } diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java deleted file mode 100644 index c9ffbba0..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseSystem.java +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package org.terasology.module.lightandshadow.phases; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.terasology.engine.entitySystem.entity.EntityRef; -import org.terasology.engine.entitySystem.systems.BaseComponentSystem; -import org.terasology.engine.entitySystem.systems.RegisterSystem; -import org.terasology.engine.registry.In; -import org.terasology.engine.registry.Share; -import org.terasology.module.lightandshadow.systems.GameEntitySystem; - -/** - * Provides an entity that keeps track of game state information. - */ -@RegisterSystem -@Share(value = PhaseSystem.class) -public class PhaseSystem extends BaseComponentSystem { - - private static final Logger logger = LoggerFactory.getLogger(PhaseSystem.class); - - @In - GameEntitySystem gameEntitySystem; - - @Override - public void initialise() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.addOrSaveComponent(new PhaseComponent()); - } - - // TODO: What happens here if there's multiple components present that implement PhaseComponent? - public Phase getCurrentPhase() { - return gameEntitySystem.getGameEntity().getComponent(PhaseComponent.class).getCurrentPhase(); - } - - void transitionPhase(Phase from, Phase to) { - logger.debug("Transitioning phases from " + from.toString() + " to " + to.toString()); - switch (from) { - case IDLE: - endIdlePhase(); - break; - case PRE_GAME: - endPreGamePhase(); - break; - case COUNTDOWN: - endCountdownPhase(); - break; - case IN_GAME: - endInGamePhase(); - break; - case POST_GAME: - endPostGamePhase(); - break; - default: - logger.debug("Attempted to transition from invalid phase " + to.toString()); - } - - switch (to) { - case IDLE: - startIdlePhase(); - break; - case PRE_GAME: - startPreGamePhase(); - break; - case COUNTDOWN: - startCountdownPhase(); - break; - case IN_GAME: - startInGamePhase(); - break; - case POST_GAME: - startPostGamePhase(); - break; - default: - logger.debug("Attempted to transition into invalid phase " + to.toString()); - } - }; - - private void assertInPhase(Phase expectedPhase, EntityRef gameEntity) { - PhaseComponent phase = gameEntity.getComponent(PhaseComponent.class); - - if (phase == null || phase.getCurrentPhase() != expectedPhase) { - String message = "LightAndShadow/PhaseSystem: Attempting to end idle phase without being in `" + expectedPhase + "` phase."; - String entityDump = gameEntity.toFullDescription(); - throw new RuntimeException(message + "\n" + entityDump); - } - gameEntity.send(new OnIdlePhaseEndedEvent()); - } - - private void endIdlePhase() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - assertInPhase(Phase.IDLE, gameEntity); - gameEntity.send(new OnIdlePhaseEndedEvent()); - }; - - private void endPreGamePhase() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - assertInPhase(Phase.PRE_GAME, gameEntity); - gameEntity.send(new OnPreGamePhaseEndedEvent()); - }; - - private void endCountdownPhase() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - assertInPhase(Phase.COUNTDOWN, gameEntity); - gameEntity.send(new OnCountdownPhaseEndedEvent()); - }; - - private void endInGamePhase() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - assertInPhase(Phase.IN_GAME, gameEntity); - gameEntity.send(new OnInGamePhaseEndedEvent()); - }; - - private void endPostGamePhase() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - assertInPhase(Phase.POST_GAME, gameEntity); - gameEntity.send(new OnPostGamePhaseEndedEvent()); - }; - - private void startIdlePhase() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.updateComponent(PhaseComponent.class, phaseComponent -> { - phaseComponent.setCurrentPhase(Phase.IDLE); - return phaseComponent; - }); - gameEntity.send(new OnIdlePhaseStartedEvent()); - }; - - private void startPreGamePhase() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.updateComponent(PhaseComponent.class, phaseComponent -> { - phaseComponent.setCurrentPhase(Phase.PRE_GAME); - return phaseComponent; - }); - gameEntity.send(new OnPreGamePhaseStartedEvent()); - }; - - private void startCountdownPhase() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.updateComponent(PhaseComponent.class, phaseComponent -> { - phaseComponent.setCurrentPhase(Phase.COUNTDOWN); - return phaseComponent; - }); - gameEntity.send(new OnCountdownPhaseStartedEvent()); - }; - - private void startInGamePhase() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.updateComponent(PhaseComponent.class, phaseComponent -> { - phaseComponent.setCurrentPhase(Phase.IN_GAME); - return phaseComponent; - }); - gameEntity.send(new OnInGamePhaseStartedEvent()); - }; - - private void startPostGamePhase() { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - gameEntity.updateComponent(PhaseComponent.class, phaseComponent -> { - phaseComponent.setCurrentPhase(Phase.POST_GAME); - return phaseComponent; - }); - gameEntity.send(new OnPostGamePhaseStartedEvent()); - }; -} diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystem.java similarity index 94% rename from src/main/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystem.java rename to src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystem.java index 299fa328..e74defe4 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystem.java @@ -1,7 +1,7 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.phases; +package org.terasology.module.lightandshadow.phases.authority; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +13,7 @@ import org.terasology.engine.logic.permission.PermissionManager; import org.terasology.engine.registry.In; import org.terasology.module.inventory.components.ItemCommands; +import org.terasology.module.lightandshadow.phases.Phase; public class PhaseCommandsSystem extends BaseComponentSystem { diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseComponent.java b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseComponent.java similarity index 87% rename from src/main/java/org/terasology/module/lightandshadow/phases/PhaseComponent.java rename to src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseComponent.java index 697a95d0..75ed839f 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseComponent.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseComponent.java @@ -1,9 +1,10 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.phases; +package org.terasology.module.lightandshadow.phases.authority; import org.terasology.gestalt.entitysystem.component.Component; +import org.terasology.module.lightandshadow.phases.Phase; /** * Read-only component holding the current state of a Light and Shadow game. diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java new file mode 100644 index 00000000..f25c7aa8 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java @@ -0,0 +1,81 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.phases.authority; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.systems.BaseComponentSystem; +import org.terasology.engine.entitySystem.systems.RegisterMode; +import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.registry.In; +import org.terasology.engine.registry.Share; +import org.terasology.module.lightandshadow.phases.OnCountdownPhaseEndedEvent; +import org.terasology.module.lightandshadow.phases.OnCountdownPhaseStartedEvent; +import org.terasology.module.lightandshadow.phases.OnIdlePhaseEndedEvent; +import org.terasology.module.lightandshadow.phases.OnIdlePhaseStartedEvent; +import org.terasology.module.lightandshadow.phases.OnInGamePhaseEndedEvent; +import org.terasology.module.lightandshadow.phases.OnInGamePhaseStartedEvent; +import org.terasology.module.lightandshadow.phases.OnPostGamePhaseEndedEvent; +import org.terasology.module.lightandshadow.phases.OnPostGamePhaseStartedEvent; +import org.terasology.module.lightandshadow.phases.OnPreGamePhaseEndedEvent; +import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; +import org.terasology.module.lightandshadow.phases.Phase; +import org.terasology.module.lightandshadow.systems.GameEntitySystem; + +/** + * Provides an entity that keeps track of game state information. + */ +@RegisterSystem(RegisterMode.AUTHORITY) +@Share(value = PhaseSystem.class) +public class PhaseSystem extends BaseComponentSystem { + + private static final Logger logger = LoggerFactory.getLogger(PhaseSystem.class); + + @In + GameEntitySystem gameEntitySystem; + + @Override + public void initialise() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + gameEntity.addOrSaveComponent(new PhaseComponent()); + } + + // TODO: What happens here if there's multiple components present that implement PhaseComponent? + public Phase getCurrentPhase() { + return gameEntitySystem.getGameEntity().getComponent(PhaseComponent.class).getCurrentPhase(); + } + + void transitionPhase(Phase from, Phase to) { + logger.debug("Transitioning phases from " + from + " to " + to); + endPhase(from); + startPhase(to); + }; + + private void assertInPhase(Phase expectedPhase, EntityRef gameEntity) { + PhaseComponent phase = gameEntity.getComponent(PhaseComponent.class); + + if (phase == null || phase.getCurrentPhase() != expectedPhase) { + String message = "LightAndShadow/PhaseSystem: Attempting to end idle phase without being in `" + expectedPhase + "` phase."; + String entityDump = gameEntity.toFullDescription(); + throw new RuntimeException(message + "\n" + entityDump); + } + gameEntity.send(new OnIdlePhaseEndedEvent()); + } + + private void endPhase(Phase endingPhase) { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + assertInPhase(endingPhase, gameEntity); + gameEntity.send(endingPhase.endEvent.get()); + } + + private void startPhase(Phase startingPhase) { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + gameEntity.updateComponent(PhaseComponent.class, phaseComponent -> { + phaseComponent.setCurrentPhase(startingPhase); + return phaseComponent; + }); + gameEntity.send(startingPhase.startEvent.get()); + } +} diff --git a/src/test/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java similarity index 92% rename from src/test/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystemTest.java rename to src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java index 6dad8d03..9695ac12 100644 --- a/src/test/java/org/terasology/module/lightandshadow/phases/PhaseCommandsSystemTest.java +++ b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java @@ -1,13 +1,14 @@ // Copyright 2021 The Terasology Foundation // SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.phases; +package org.terasology.module.lightandshadow.phases.authority; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.entity.internal.PojoEntityManager; +import org.terasology.module.lightandshadow.phases.Phase; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; From 13cc215483d2b0bac25217c38ae201c28fd26720 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Fri, 17 Dec 2021 23:24:39 +0100 Subject: [PATCH 09/51] fix: phase transition command --- .../phases/authority/PhaseCommandsSystem.java | 6 +++++- .../module/lightandshadow/phases/authority/PhaseSystem.java | 4 +++- .../phases/authority/PhaseCommandsSystemTest.java | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystem.java index e74defe4..8f7792e3 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystem.java @@ -7,6 +7,8 @@ import org.slf4j.LoggerFactory; import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.systems.BaseComponentSystem; +import org.terasology.engine.entitySystem.systems.RegisterMode; +import org.terasology.engine.entitySystem.systems.RegisterSystem; import org.terasology.engine.logic.console.commandSystem.annotations.Command; import org.terasology.engine.logic.console.commandSystem.annotations.CommandParam; import org.terasology.engine.logic.console.commandSystem.annotations.Sender; @@ -15,6 +17,7 @@ import org.terasology.module.inventory.components.ItemCommands; import org.terasology.module.lightandshadow.phases.Phase; +@RegisterSystem(RegisterMode.ALWAYS) public class PhaseCommandsSystem extends BaseComponentSystem { private static final Logger logger = LoggerFactory.getLogger(ItemCommands.class); @@ -28,8 +31,9 @@ public class PhaseCommandsSystem extends BaseComponentSystem { requiredPermission = PermissionManager.CHEAT_PERMISSION) public String forcePhase( @Sender EntityRef client, - @CommandParam("targetPhase") Phase targetPhase) { + @CommandParam("targetPhase") String target) { + Phase targetPhase = Phase.valueOf(target.toUpperCase()); switch (targetPhase) { case IDLE: case PRE_GAME: diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java index f25c7aa8..fe8d9d3c 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java @@ -37,9 +37,11 @@ public class PhaseSystem extends BaseComponentSystem { GameEntitySystem gameEntitySystem; @Override - public void initialise() { + public void postBegin() { EntityRef gameEntity = gameEntitySystem.getGameEntity(); gameEntity.addOrSaveComponent(new PhaseComponent()); + logger.info("hello dog"); + logger.debug("Initializing PhaseSystem with game entity:\n{}", gameEntity.toFullDescription()); } // TODO: What happens here if there's multiple components present that implement PhaseComponent? diff --git a/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java index 9695ac12..977265d4 100644 --- a/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java +++ b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java @@ -34,7 +34,7 @@ private void createMockPhaseSystem(EntityRef gameEntity) { @ParameterizedTest @EnumSource(Phase.class) void testForceIdlePhaseSuccessful(Phase currentPhase) { - phaseCommandsSystem.forcePhase(EntityRef.NULL, Phase.IDLE); // TODO: use non-NULL entity + phaseCommandsSystem.forcePhase(EntityRef.NULL, "IDLE"); // TODO: use non-NULL entity verify(phaseCommandsSystem.phaseSystem, times(1)).transitionPhase(currentPhase, Phase.IDLE); } From 808f2c0630af8bf2385b4e8e8c62689bac1af3c4 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Fri, 17 Dec 2021 23:48:37 +0100 Subject: [PATCH 10/51] fix: phase transition command test --- .../authority/PhaseCommandsSystemTest.java | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java index 977265d4..4b2a26ba 100644 --- a/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java +++ b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java @@ -3,12 +3,18 @@ package org.terasology.module.lightandshadow.phases.authority; +import org.codehaus.plexus.util.cli.Arg; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.MethodSource; import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.entity.internal.PojoEntityManager; import org.terasology.module.lightandshadow.phases.Phase; +import org.terasology.module.lightandshadow.systems.GameEntitySystem; + +import java.util.stream.Stream; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -28,14 +34,31 @@ void setUp() { private void createMockPhaseSystem(EntityRef gameEntity) { phaseCommandsSystem.phaseSystem = mock(PhaseSystem.class); + phaseCommandsSystem.phaseSystem.gameEntitySystem = mock(GameEntitySystem.class); when(phaseCommandsSystem.phaseSystem.gameEntitySystem.getGameEntity()).thenReturn(gameEntity); } + static Stream phaseProvider() { + Stream.Builder argumentsBuilder = Stream.builder(); + for (Phase fromPhase : Phase.values()) { + for (Phase toPhase : Phase.values()) { + argumentsBuilder.add(Arguments.of(fromPhase, toPhase)); + } + } + + return argumentsBuilder.build(); + } + @ParameterizedTest - @EnumSource(Phase.class) - void testForceIdlePhaseSuccessful(Phase currentPhase) { - phaseCommandsSystem.forcePhase(EntityRef.NULL, "IDLE"); // TODO: use non-NULL entity - verify(phaseCommandsSystem.phaseSystem, times(1)).transitionPhase(currentPhase, Phase.IDLE); + @MethodSource("phaseProvider") + void testForceIdlePhaseSuccessful(Phase currentPhase, Phase targetPhase) { + // Arrange + when(phaseCommandsSystem.phaseSystem.getCurrentPhase()).thenReturn(currentPhase); + // Act + phaseCommandsSystem.forcePhase(EntityRef.NULL, targetPhase.toString()); // TODO: use non-NULL entity + // Assert + verify(phaseCommandsSystem.phaseSystem, times(1)).getCurrentPhase(); + verify(phaseCommandsSystem.phaseSystem, times(1)).transitionPhase(currentPhase, targetPhase); } } \ No newline at end of file From 912ac042adc9cfd6bacec67e24ae4adc45190f19 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sat, 18 Dec 2021 00:03:10 +0100 Subject: [PATCH 11/51] test: add negative forcePhase command test for non-enum target phase --- .../phases/authority/PhaseCommandsSystemTest.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java index 4b2a26ba..408195dd 100644 --- a/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java +++ b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java @@ -17,8 +17,11 @@ import java.util.stream.Stream; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; class PhaseCommandsSystemTest { @@ -51,7 +54,7 @@ static Stream phaseProvider() { @ParameterizedTest @MethodSource("phaseProvider") - void testForceIdlePhaseSuccessful(Phase currentPhase, Phase targetPhase) { + void testForcePhaseSuccessful(Phase currentPhase, Phase targetPhase) { // Arrange when(phaseCommandsSystem.phaseSystem.getCurrentPhase()).thenReturn(currentPhase); // Act @@ -61,4 +64,11 @@ void testForceIdlePhaseSuccessful(Phase currentPhase, Phase targetPhase) { verify(phaseCommandsSystem.phaseSystem, times(1)).transitionPhase(currentPhase, targetPhase); } + @ParameterizedTest + @EnumSource(Phase.class) + void testForcePhaseFailedNonExistingTargetPhase(Phase currentPhase) { + when(phaseCommandsSystem.phaseSystem.getCurrentPhase()).thenReturn(currentPhase); + phaseCommandsSystem.forcePhase(EntityRef.NULL, "foobar"); + verifyNoInteractions(phaseCommandsSystem.phaseSystem); + } } \ No newline at end of file From 44c23a9b1cee019be077932d2ef5a9d79d043c51 Mon Sep 17 00:00:00 2001 From: Tobias Nett Date: Sat, 18 Dec 2021 01:15:37 +0100 Subject: [PATCH 12/51] introduce PhaseParameterAdapter to allow for `Phase` instances as command arguments The parsing of the in-game console arguments is handled by central `ParameterAdapter`s. Modules can register new adapters to extend the support for custom types. This adds support for the `Phase` enum to be used in the in-game console. --- .../phases/PhaseParameterAdapter.java | 36 ++++++++++++++++ .../phases/PhaseParameterAdapterTest.java | 41 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/main/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapter.java create mode 100644 src/test/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapterTest.java diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapter.java b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapter.java new file mode 100644 index 00000000..44194085 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapter.java @@ -0,0 +1,36 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.phases; + +import org.terasology.engine.logic.console.commandSystem.adapter.ParameterAdapter; +import org.terasology.engine.logic.console.commandSystem.adapter.ParameterAdapterManager; + +/** + * Adapter to allow direct parsing of {@link Phase} arguments in the in-game console. + *

+ * If this adapter is registered with the {@link ParameterAdapterManager}, a command can accept a {@link Phase} as input parameter without + * the need to manually parse it. + *

{@code
+ *      public String myPhaseCommand(
+ *             @Sender EntityRef client,
+ *             @CommandParam("phase") Phase phase) { ... }
+ * }
+ * + * This adapter can parse exact matches that conform to {@link Phase#valueOf(String)}. + * It is case-insensitive, e.g., it compensates lower-case characters. + */ +public class PhaseParameterAdapter implements ParameterAdapter { + + @Override + public Phase parse(String raw) { + // TODO: Make the user aware of the valid values. Needs some adjustments in CommandParameter. + // "Unknown phase ''. Valid values are: " + return Phase.valueOf(raw.toUpperCase()); + } + + @Override + public String convertToString(Phase value) { + return value.toString(); + } +} diff --git a/src/test/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapterTest.java b/src/test/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapterTest.java new file mode 100644 index 00000000..530cd6b4 --- /dev/null +++ b/src/test/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapterTest.java @@ -0,0 +1,41 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.phases; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class PhaseParameterAdapterTest { + + static PhaseParameterAdapter adapter; + + @BeforeAll + static void setup() { + // the adapter is a stateless component and can therefore safely be reused. + adapter = new PhaseParameterAdapter(); + } + + @Test + void testInvalidInput() { + assertThrows(IllegalArgumentException.class, () -> adapter.parse("foobar")); + } + + @ParameterizedTest + @EnumSource(Phase.class) + void testExactInput(Phase phase) { + assertEquals(phase, adapter.parse(phase.toString())); + } + + @ParameterizedTest + @EnumSource(Phase.class) + void testLowercaseInput(Phase phase) { + assertEquals(phase, adapter.parse(phase.toString().toLowerCase())); + } +} \ No newline at end of file From baf45c9060de8722249c4ed904db6090c28bccd7 Mon Sep 17 00:00:00 2001 From: Tobias Nett Date: Sat, 18 Dec 2021 01:33:40 +0100 Subject: [PATCH 13/51] improve PhaseCommandsSystem and register parameter adapter --- .../phases/authority/PhaseCommandsSystem.java | 17 +++++++++++++++-- .../authority/PhaseCommandsSystemTest.java | 15 +-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystem.java index 8f7792e3..28975541 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystem.java @@ -9,6 +9,7 @@ import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterMode; import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.logic.console.commandSystem.adapter.ParameterAdapterManager; import org.terasology.engine.logic.console.commandSystem.annotations.Command; import org.terasology.engine.logic.console.commandSystem.annotations.CommandParam; import org.terasology.engine.logic.console.commandSystem.annotations.Sender; @@ -16,6 +17,7 @@ import org.terasology.engine.registry.In; import org.terasology.module.inventory.components.ItemCommands; import org.terasology.module.lightandshadow.phases.Phase; +import org.terasology.module.lightandshadow.phases.PhaseParameterAdapter; @RegisterSystem(RegisterMode.ALWAYS) public class PhaseCommandsSystem extends BaseComponentSystem { @@ -25,15 +27,23 @@ public class PhaseCommandsSystem extends BaseComponentSystem { @In PhaseSystem phaseSystem; + @In + ParameterAdapterManager parameterAdapterManager; + + @Override + public void initialise() { + // globally register the adapter for `Phase` values. Required for `forcePhase` command. + parameterAdapterManager.registerAdapter(Phase.class, new PhaseParameterAdapter()); + } + @Command(shortDescription = "Forces a phase transition to a specified target phase", helpText = "Puts the Light & Shadow World into a desired phase", runOnServer = true, requiredPermission = PermissionManager.CHEAT_PERMISSION) public String forcePhase( @Sender EntityRef client, - @CommandParam("targetPhase") String target) { + @CommandParam("targetPhase") Phase targetPhase) { - Phase targetPhase = Phase.valueOf(target.toUpperCase()); switch (targetPhase) { case IDLE: case PRE_GAME: @@ -42,6 +52,9 @@ public String forcePhase( case POST_GAME: break; default: + // this case can only occur if we add a new phase and (deliberately or not) don't add it here. + // TODO(skaldarnar): I'd like to remove this altogether, as I think this is just maintenance overhead and a potential + // pitfall in the case we're adding a new phase. Less code is better code. StringBuilder errorMsg = new StringBuilder(targetPhase + " is not a valid phase, please choose one of the following:"); for (Phase validPhase : Phase.values()) { errorMsg.append(" ").append(validPhase.toString()); diff --git a/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java index 408195dd..c0eadec4 100644 --- a/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java +++ b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java @@ -3,11 +3,9 @@ package org.terasology.module.lightandshadow.phases.authority; -import org.codehaus.plexus.util.cli.Arg; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.entity.internal.PojoEntityManager; @@ -17,11 +15,8 @@ import java.util.stream.Stream; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; class PhaseCommandsSystemTest { @@ -58,17 +53,9 @@ void testForcePhaseSuccessful(Phase currentPhase, Phase targetPhase) { // Arrange when(phaseCommandsSystem.phaseSystem.getCurrentPhase()).thenReturn(currentPhase); // Act - phaseCommandsSystem.forcePhase(EntityRef.NULL, targetPhase.toString()); // TODO: use non-NULL entity + phaseCommandsSystem.forcePhase(EntityRef.NULL, targetPhase); // Assert verify(phaseCommandsSystem.phaseSystem, times(1)).getCurrentPhase(); verify(phaseCommandsSystem.phaseSystem, times(1)).transitionPhase(currentPhase, targetPhase); } - - @ParameterizedTest - @EnumSource(Phase.class) - void testForcePhaseFailedNonExistingTargetPhase(Phase currentPhase) { - when(phaseCommandsSystem.phaseSystem.getCurrentPhase()).thenReturn(currentPhase); - phaseCommandsSystem.forcePhase(EntityRef.NULL, "foobar"); - verifyNoInteractions(phaseCommandsSystem.phaseSystem); - } } \ No newline at end of file From 3998e125c8757e85554160e46dbb9a368c794aac Mon Sep 17 00:00:00 2001 From: Tobias Nett Date: Sat, 18 Dec 2021 12:22:07 +0100 Subject: [PATCH 14/51] fix checkstyle issues --- .../lightandshadow/phases/PhaseParameterAdapterTest.java | 3 +-- .../phases/authority/PhaseCommandsSystemTest.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapterTest.java b/src/test/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapterTest.java index 530cd6b4..febbf5b5 100644 --- a/src/test/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapterTest.java +++ b/src/test/java/org/terasology/module/lightandshadow/phases/PhaseParameterAdapterTest.java @@ -4,7 +4,6 @@ package org.terasology.module.lightandshadow.phases; import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; @@ -38,4 +37,4 @@ void testExactInput(Phase phase) { void testLowercaseInput(Phase phase) { assertEquals(phase, adapter.parse(phase.toString().toLowerCase())); } -} \ No newline at end of file +} diff --git a/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java index c0eadec4..657f7672 100644 --- a/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java +++ b/src/test/java/org/terasology/module/lightandshadow/phases/authority/PhaseCommandsSystemTest.java @@ -58,4 +58,4 @@ void testForcePhaseSuccessful(Phase currentPhase, Phase targetPhase) { verify(phaseCommandsSystem.phaseSystem, times(1)).getCurrentPhase(); verify(phaseCommandsSystem.phaseSystem, times(1)).transitionPhase(currentPhase, targetPhase); } -} \ No newline at end of file +} From 9ec8aba307ffcf8a7bf667c8119b17bc8d29c933 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Wed, 31 Aug 2022 21:26:32 +0200 Subject: [PATCH 15/51] feat: add SwitchToPhaseEvent for systems to trigger phase transition --- .../phases/SwitchToPhaseEvent.java | 16 ++++++++++++++++ .../phases/authority/PhaseSystem.java | 9 +++++++++ 2 files changed, 25 insertions(+) create mode 100644 src/main/java/org/terasology/module/lightandshadow/phases/SwitchToPhaseEvent.java diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/SwitchToPhaseEvent.java b/src/main/java/org/terasology/module/lightandshadow/phases/SwitchToPhaseEvent.java new file mode 100644 index 00000000..639994c9 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/phases/SwitchToPhaseEvent.java @@ -0,0 +1,16 @@ +// Copyright 2022 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.phases; + +import org.terasology.gestalt.entitysystem.event.Event; + +public class SwitchToPhaseEvent implements Event { + public Phase targetPhase; + + public SwitchToPhaseEvent() { } + + public SwitchToPhaseEvent(Phase targetPhase) { + this.targetPhase = targetPhase; + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java index fe8d9d3c..a4f46b22 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java @@ -9,8 +9,11 @@ import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterMode; import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.logic.common.ActivateEvent; import org.terasology.engine.registry.In; import org.terasology.engine.registry.Share; +import org.terasology.gestalt.entitysystem.event.ReceiveEvent; +import org.terasology.lightandshadowresources.components.SetTeamOnActivateComponent; import org.terasology.module.lightandshadow.phases.OnCountdownPhaseEndedEvent; import org.terasology.module.lightandshadow.phases.OnCountdownPhaseStartedEvent; import org.terasology.module.lightandshadow.phases.OnIdlePhaseEndedEvent; @@ -22,6 +25,7 @@ import org.terasology.module.lightandshadow.phases.OnPreGamePhaseEndedEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; import org.terasology.module.lightandshadow.phases.Phase; +import org.terasology.module.lightandshadow.phases.SwitchToPhaseEvent; import org.terasology.module.lightandshadow.systems.GameEntitySystem; /** @@ -55,6 +59,11 @@ void transitionPhase(Phase from, Phase to) { startPhase(to); }; + @ReceiveEvent(components = PhaseComponent.class) + public void switchToPhase(SwitchToPhaseEvent event, EntityRef entity) { + transitionPhase(getCurrentPhase(), event.targetPhase); + } + private void assertInPhase(Phase expectedPhase, EntityRef gameEntity) { PhaseComponent phase = gameEntity.getComponent(PhaseComponent.class); From 0d4581fb4bb2dcf3c42cd94246ef48351920a797 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Wed, 31 Aug 2022 21:31:31 +0200 Subject: [PATCH 16/51] refactor: split up PregameSystem on AttackSystem and MagicDomeSystem --- .../events/ActivateBarrierEvent.java | 11 ----- .../lightandshadow/systems/AttackSystem.java | 19 +++++++- .../systems/MagicDomeSystem.java | 14 +++--- .../lightandshadow/systems/PregameSystem.java | 44 ------------------- 4 files changed, 26 insertions(+), 62 deletions(-) delete mode 100644 src/main/java/org/terasology/module/lightandshadow/events/ActivateBarrierEvent.java delete mode 100644 src/main/java/org/terasology/module/lightandshadow/systems/PregameSystem.java diff --git a/src/main/java/org/terasology/module/lightandshadow/events/ActivateBarrierEvent.java b/src/main/java/org/terasology/module/lightandshadow/events/ActivateBarrierEvent.java deleted file mode 100644 index 999570ff..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/events/ActivateBarrierEvent.java +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.events; - -import org.terasology.gestalt.entitysystem.event.Event; - -/** - * Trigger event to activate the pregame barriers. - */ -public class ActivateBarrierEvent implements Event { -} diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java index db27040e..5d364c43 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java @@ -5,6 +5,8 @@ import org.terasology.engine.entitySystem.entity.EntityManager; import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.event.EventPriority; +import org.terasology.engine.entitySystem.event.Priority; import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterMode; import org.terasology.engine.entitySystem.systems.RegisterSystem; @@ -17,13 +19,17 @@ import org.terasology.gestalt.entitysystem.event.ReceiveEvent; import org.terasology.lightandshadowresources.components.FlagComponent; import org.terasology.lightandshadowresources.components.LASTeamComponent; +import org.terasology.module.health.events.BeforeDamagedEvent; +import org.terasology.module.inventory.events.InventorySlotChangedEvent; import org.terasology.module.lightandshadow.components.FlagDropOnActivateComponent; import org.terasology.module.lightandshadow.components.HasFlagComponent; +import org.terasology.module.lightandshadow.components.InvulnerableComponent; import org.terasology.module.lightandshadow.events.DropFlagEvent; import org.terasology.module.lightandshadow.events.OnFlagDropEvent; import org.terasology.module.lightandshadow.events.OnFlagPickupEvent; +import org.terasology.module.lightandshadow.events.PregameEvent; import org.terasology.module.lightandshadow.events.ReturnFlagEvent; -import org.terasology.module.inventory.events.InventorySlotChangedEvent; +import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; @RegisterSystem(RegisterMode.AUTHORITY) public class AttackSystem extends BaseComponentSystem { @@ -71,6 +77,17 @@ private void dropFlagOnPlayerAttack(ActivateEvent event, EntityRef targetPlayer) } } + @ReceiveEvent + public void onPregameStart(PregameEvent event, EntityRef entity) { + entity.addComponent(new InvulnerableComponent()); + } + + @Priority(EventPriority.PRIORITY_HIGH) + @ReceiveEvent(components = InvulnerableComponent.class) + public void preventFriendlyFire(BeforeDamagedEvent event, EntityRef entity) { + event.consume(); + } + /** * Checks if player picks up flag of the same team. * If so, moves flag back to base, otherwise adds particle emitter and HasFlagComponent to player diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java index 0a3f3883..a252943d 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java @@ -21,8 +21,9 @@ import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.components.InvulnerableComponent; import org.terasology.module.lightandshadow.components.MagicDome; -import org.terasology.module.lightandshadow.events.ActivateBarrierEvent; import org.terasology.module.lightandshadow.events.DelayedDeactivateBarrierEvent; +import org.terasology.module.lightandshadow.events.PregameEvent; +import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; @RegisterSystem(RegisterMode.AUTHORITY) public class MagicDomeSystem extends BaseComponentSystem { @@ -41,18 +42,19 @@ public class MagicDomeSystem extends BaseComponentSystem { /** * Activates the barriers for the pregame regions corresponding to both the teams only in the beginning when * the barriers haven't been created yet. - * - * @param event - * @param entity */ - @ReceiveEvent - public void activateBarriers(ActivateBarrierEvent event, EntityRef entity) { + private void activateBarriers() { if (redBarrier == EntityRef.NULL && blackBarrier == EntityRef.NULL) { redBarrier = createBarrier("lightAndShadowResources:magicDome", LASUtils.CENTER_RED_BASE_POSITION, "red"); blackBarrier = createBarrier("lightAndShadowResources:magicDome", LASUtils.CENTER_BLACK_BASE_POSITION, "black"); } } + @ReceiveEvent + public void onPregameStart(PregameEvent event, EntityRef entity) { + activateBarriers(); + } + @ReceiveEvent public void delayedDeactivateBarriers(DelayedDeactivateBarrierEvent event, EntityRef entity) { delayManager.addDelayedAction(entity, DEACTIVATE_BARRIERS_ACTION, event.getDelay()); diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/PregameSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/PregameSystem.java deleted file mode 100644 index cb091405..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/systems/PregameSystem.java +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.systems; - -import org.terasology.engine.entitySystem.entity.EntityManager; -import org.terasology.engine.entitySystem.entity.EntityRef; -import org.terasology.engine.entitySystem.event.EventPriority; -import org.terasology.engine.entitySystem.event.Priority; -import org.terasology.engine.entitySystem.prefab.Prefab; -import org.terasology.engine.entitySystem.systems.BaseComponentSystem; -import org.terasology.engine.entitySystem.systems.RegisterMode; -import org.terasology.engine.entitySystem.systems.RegisterSystem; -import org.terasology.engine.registry.In; -import org.terasology.engine.utilities.Assets; -import org.terasology.gestalt.entitysystem.event.ReceiveEvent; -import org.terasology.module.health.events.BeforeDamagedEvent; -import org.terasology.module.inventory.components.StartingInventoryComponent; -import org.terasology.module.lightandshadow.components.InvulnerableComponent; -import org.terasology.module.lightandshadow.events.ActivateBarrierEvent; -import org.terasology.module.lightandshadow.events.PregameEvent; - -import java.util.Optional; - -@RegisterSystem(RegisterMode.AUTHORITY) -public class PregameSystem extends BaseComponentSystem { - Optional prefab = Assets.getPrefab("inventory"); - StartingInventoryComponent startingInventory = prefab.get().getComponent(StartingInventoryComponent.class); - - @In - private EntityManager entityManager; - - @ReceiveEvent - public void onPregameStart(PregameEvent event, EntityRef entity) { - entity.send(new ActivateBarrierEvent()); - entity.addComponent(new InvulnerableComponent()); - } - - @Priority(EventPriority.PRIORITY_HIGH) - @ReceiveEvent(components = InvulnerableComponent.class) - public void preventFriendlyFire(BeforeDamagedEvent event, EntityRef entity) { - event.consume(); - } - -} From f40653c589f749057a04f9285b06fb7acb75715c Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Wed, 31 Aug 2022 21:43:15 +0200 Subject: [PATCH 17/51] feat: switch to Pre-Game Phase on first player joining game - replace `PregameEvent` with `OnPreGamePhaseStartedEvent` --- .../module/lightandshadow/events/PregameEvent.java | 11 ----------- .../module/lightandshadow/systems/AttackSystem.java | 3 +-- .../lightandshadow/systems/MagicDomeSystem.java | 3 +-- .../lightandshadow/systems/TeleporterSystem.java | 11 +++++++++-- 4 files changed, 11 insertions(+), 17 deletions(-) delete mode 100644 src/main/java/org/terasology/module/lightandshadow/events/PregameEvent.java diff --git a/src/main/java/org/terasology/module/lightandshadow/events/PregameEvent.java b/src/main/java/org/terasology/module/lightandshadow/events/PregameEvent.java deleted file mode 100644 index 9be90421..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/events/PregameEvent.java +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.events; - -import org.terasology.gestalt.entitysystem.event.Event; - -/** - * Notification event that indicates the pregame phase has started. - */ -public class PregameEvent implements Event { -} diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java index 5d364c43..d2ba2545 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java @@ -27,7 +27,6 @@ import org.terasology.module.lightandshadow.events.DropFlagEvent; import org.terasology.module.lightandshadow.events.OnFlagDropEvent; import org.terasology.module.lightandshadow.events.OnFlagPickupEvent; -import org.terasology.module.lightandshadow.events.PregameEvent; import org.terasology.module.lightandshadow.events.ReturnFlagEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; @@ -78,7 +77,7 @@ private void dropFlagOnPlayerAttack(ActivateEvent event, EntityRef targetPlayer) } @ReceiveEvent - public void onPregameStart(PregameEvent event, EntityRef entity) { + public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { entity.addComponent(new InvulnerableComponent()); } diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java index a252943d..aa166cfc 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java @@ -22,7 +22,6 @@ import org.terasology.module.lightandshadow.components.InvulnerableComponent; import org.terasology.module.lightandshadow.components.MagicDome; import org.terasology.module.lightandshadow.events.DelayedDeactivateBarrierEvent; -import org.terasology.module.lightandshadow.events.PregameEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; @RegisterSystem(RegisterMode.AUTHORITY) @@ -51,7 +50,7 @@ private void activateBarriers() { } @ReceiveEvent - public void onPregameStart(PregameEvent event, EntityRef entity) { + public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { activateBarriers(); } diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index 7117779d..3ff333fa 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -32,8 +32,10 @@ import org.terasology.module.lightandshadow.components.LASConfigComponent; import org.terasology.module.lightandshadow.events.DelayedDeactivateBarrierEvent; import org.terasology.module.lightandshadow.events.GameStartMessageEvent; -import org.terasology.module.lightandshadow.events.PregameEvent; import org.terasology.module.lightandshadow.events.TimerEvent; +import org.terasology.module.lightandshadow.phases.Phase; +import org.terasology.module.lightandshadow.phases.SwitchToPhaseEvent; +import org.terasology.module.lightandshadow.phases.authority.PhaseSystem; import java.util.Optional; import java.util.function.Supplier; @@ -50,6 +52,8 @@ public class TeleporterSystem extends BaseComponentSystem { EntityManager entityManager; @In GameEntitySystem gameEntitySystem; + @In + PhaseSystem phaseSystem; Optional prefab = Assets.getPrefab("inventory"); StartingInventoryComponent startingInventory = prefab.get().getComponent(StartingInventoryComponent.class); @@ -131,8 +135,11 @@ private String setPlayerTeamToTeleporterTeam(EntityRef player, EntityRef telepor } private void handlePlayerTeleport(EntityRef player, String team) { + // when the first player joins the game, switch to pre-game phase + if (phaseSystem.getCurrentPhase() == Phase.IDLE) { + gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.PRE_GAME)); + } Vector3f randomVector = new Vector3f(-1 + random.nextInt(3), 0, -1 + random.nextInt(3)); - player.send(new PregameEvent()); player.send(new CharacterTeleportEvent(randomVector.add(LASUtils.getTeleportDestination(team)))); player.send(new SetDirectionEvent(LASUtils.getYaw(LASUtils.getTeleportDestination(team). sub(LASUtils.getTeleportDestination(LASUtils.getOppositionTeam(team)), new Vector3f())), 0)); From 9bee4be363b9c23c849ffc291e1f3889f0b98265 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Wed, 31 Aug 2022 22:56:35 +0200 Subject: [PATCH 18/51] feat: add LASTeamStatsComponent to game entity to track team sizes - update method in GameEntitySystem that can be called to update stats on-demand --- .../components/LASTeamStatsComponent.java | 20 ++++++++++++++++ .../systems/GameEntitySystem.java | 24 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 src/main/java/org/terasology/module/lightandshadow/components/LASTeamStatsComponent.java diff --git a/src/main/java/org/terasology/module/lightandshadow/components/LASTeamStatsComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/LASTeamStatsComponent.java new file mode 100644 index 00000000..34d31017 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/components/LASTeamStatsComponent.java @@ -0,0 +1,20 @@ +// Copyright 2022 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.components; + +import org.terasology.gestalt.entitysystem.component.Component; + +public class LASTeamStatsComponent implements Component { + public int redTeamSize; + public int blackTeamSize; + public int whiteTeamSize; + + @Override + public void copyFrom(LASTeamStatsComponent other) { + this.blackTeamSize = other.blackTeamSize; + this.redTeamSize = other.redTeamSize; + this.whiteTeamSize = other.whiteTeamSize; + } + +} diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/GameEntitySystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/GameEntitySystem.java index 9148db46..c378dd0d 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/GameEntitySystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/GameEntitySystem.java @@ -12,9 +12,13 @@ import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.logic.players.PlayerCharacterComponent; import org.terasology.engine.registry.Share; +import org.terasology.lightandshadowresources.components.LASTeamComponent; +import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.components.LASConfigComponent; import org.terasology.engine.registry.In; +import org.terasology.module.lightandshadow.components.LASTeamStatsComponent; /** * Provides an entity that keeps track of game state information. @@ -43,5 +47,25 @@ public EntityRef getGameEntity() { } return gameEntity; } + + /** + * Updates the team statistics stored in the LASTeamStatsComponent of the GameEntity + */ + public void updateTeamStats() { + LASTeamStatsComponent teamStats = new LASTeamStatsComponent(); + Iterable characters = entityManager.getEntitiesWith(PlayerCharacterComponent.class, LASTeamComponent.class); + for (EntityRef character : characters) { + if (character.getComponent(LASTeamComponent.class).team.equals(LASUtils.BLACK_TEAM)) { + teamStats.blackTeamSize++; + } else if (character.getComponent(LASTeamComponent.class).team.equals(LASUtils.RED_TEAM)) { + teamStats.redTeamSize++; + } else { + logger.debug("Found character {} with white team", character.getId()); + teamStats.whiteTeamSize++; + } + } + + gameEntity.addOrSaveComponent(teamStats); + } } From 00e233565e4b60663170cfe2436ab7229379fd9a Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Wed, 31 Aug 2022 23:04:30 +0200 Subject: [PATCH 19/51] feat: make minimum team size configurable --- assets/prefabs/gameEntity.prefab | 3 ++- .../module/lightandshadow/components/LASConfigComponent.java | 2 ++ .../module/lightandshadow/systems/TeleporterSystem.java | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/assets/prefabs/gameEntity.prefab b/assets/prefabs/gameEntity.prefab index 7eb7ce75..d1be04e6 100644 --- a/assets/prefabs/gameEntity.prefab +++ b/assets/prefabs/gameEntity.prefab @@ -1,6 +1,7 @@ { "LASConfig" : { - "maxTeamSizeDifference" : 3 + "maxTeamSizeDifference": 3, + "minTeamSize": 1 }, "CurrencyStorage": { "amount": 100 diff --git a/src/main/java/org/terasology/module/lightandshadow/components/LASConfigComponent.java b/src/main/java/org/terasology/module/lightandshadow/components/LASConfigComponent.java index b2622a24..79d48d6d 100644 --- a/src/main/java/org/terasology/module/lightandshadow/components/LASConfigComponent.java +++ b/src/main/java/org/terasology/module/lightandshadow/components/LASConfigComponent.java @@ -6,9 +6,11 @@ public class LASConfigComponent implements Component { public int maxTeamSizeDifference; + public int minTeamSize; @Override public void copyFrom(LASConfigComponent other) { this.maxTeamSizeDifference = other.maxTeamSizeDifference; + this.minTeamSize = other.minTeamSize; } } diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index 3ff333fa..cefda470 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -93,6 +93,7 @@ private boolean isProperTeamSize(EntityRef teleporter, EntityRef player) { int oppositeTeamCount = 0; int teleporterTeamCount = 0; int maxTeamSizeDifference = gameEntity.getComponent(LASConfigComponent.class).maxTeamSizeDifference; + int minTeamSize = gameEntity.getComponent(LASConfigComponent.class).minTeamSize; String teleporterTeam = teleporter.getComponent(LASTeamComponent.class).team; Iterable characters = entityManager.getEntitiesWith(PlayerCharacterComponent.class, LASTeamComponent.class); @@ -106,7 +107,7 @@ private boolean isProperTeamSize(EntityRef teleporter, EntityRef player) { } } if (teleporterTeamCount - oppositeTeamCount < maxTeamSizeDifference) { - if (teleporterTeamCount >= 0 && oppositeTeamCount >= 1 && !gameStart) { + if (teleporterTeamCount >= minTeamSize - 1 && oppositeTeamCount >= minTeamSize && !gameStart) { sendEventToClients(TimerEvent::new); player.send(new DelayedDeactivateBarrierEvent(30000)); gameStart = true; From 234f7f3b0241ef5d1114c3f4bc4c69badcaa2acd Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Wed, 31 Aug 2022 23:07:15 +0200 Subject: [PATCH 20/51] refactor: split out teleport and game start condition checks, leverage team stats --- .../systems/TeleporterSystem.java | 83 ++++++++++--------- 1 file changed, 45 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index cefda470..43667978 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -4,6 +4,8 @@ import org.joml.Random; import org.joml.Vector3f; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.terasology.engine.entitySystem.entity.EntityManager; import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.prefab.Prefab; @@ -20,6 +22,7 @@ import org.terasology.engine.logic.players.PlayerCharacterComponent; import org.terasology.engine.logic.players.SetDirectionEvent; import org.terasology.engine.network.ClientComponent; +import org.terasology.engine.network.events.DisconnectedEvent; import org.terasology.engine.registry.In; import org.terasology.engine.utilities.Assets; import org.terasology.gestalt.entitysystem.event.Event; @@ -30,6 +33,7 @@ import org.terasology.module.inventory.events.RequestInventoryEvent; import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.components.LASConfigComponent; +import org.terasology.module.lightandshadow.components.LASTeamStatsComponent; import org.terasology.module.lightandshadow.events.DelayedDeactivateBarrierEvent; import org.terasology.module.lightandshadow.events.GameStartMessageEvent; import org.terasology.module.lightandshadow.events.TimerEvent; @@ -55,6 +59,8 @@ public class TeleporterSystem extends BaseComponentSystem { @In PhaseSystem phaseSystem; + private static final Logger logger = LoggerFactory.getLogger(TeleporterSystem.class); + Optional prefab = Assets.getPrefab("inventory"); StartingInventoryComponent startingInventory = prefab.get().getComponent(StartingInventoryComponent.class); @@ -80,51 +86,52 @@ public String setMaxTeamSizeDifference(@Sender EntityRef client, @CommandParam(" * @param entity */ @ReceiveEvent(components = SetTeamOnActivateComponent.class) - public void onActivate(ActivateEvent event, EntityRef entity) { + public void onTeleportActivation(ActivateEvent event, EntityRef entity) { EntityRef player = event.getInstigator(); - if (isProperTeamSize(entity, player)) { - String team = setPlayerTeamToTeleporterTeam(player, entity); - handlePlayerTeleport(player, team); + String targetTeam = entity.getComponent(LASTeamComponent.class).team; + if (targetTeam.equals(LASUtils.WHITE_TEAM)) { + // teleporters are (currently) expected to only target black or red team + logger.debug("Player {} attempted to join white team", player.getId()); + event.consume(); + } else { + gameEntitySystem.updateTeamStats(); + + if (isBalancedTeams(targetTeam)) { + String team = setPlayerTeamToTeleporterTeam(player, entity); + handlePlayerTeleport(player, team); + + // check game start condition + if (isMinSizeTeams() && !gameStart) { + sendEventToClients(TimerEvent::new); + player.send(new DelayedDeactivateBarrierEvent(30000)); + gameStart = true; + } + } else { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + LASConfigComponent config = gameEntity.getComponent(LASConfigComponent.class); + player.getOwner().send(new ChatMessageEvent( + "The " + targetTeam + " team has " + config.maxTeamSizeDifference + " player(s) more, " + + "please join the " + LASUtils.getOppositionTeam(targetTeam) + " team.", EntityRef.NULL)); + } } } - private boolean isProperTeamSize(EntityRef teleporter, EntityRef player) { + private boolean isBalancedTeams(String targetTeam) { EntityRef gameEntity = gameEntitySystem.getGameEntity(); - int oppositeTeamCount = 0; - int teleporterTeamCount = 0; + LASTeamStatsComponent teamStats = gameEntity.getComponent(LASTeamStatsComponent.class); int maxTeamSizeDifference = gameEntity.getComponent(LASConfigComponent.class).maxTeamSizeDifference; + int currentTeamSizeDiff = teamStats.blackTeamSize - teamStats.redTeamSize; + int postTeleportTeamSizeDiff = targetTeam.equals(LASUtils.BLACK_TEAM) ? currentTeamSizeDiff + 1 : currentTeamSizeDiff - 1; + + return Math.abs(postTeleportTeamSizeDiff) <= maxTeamSizeDifference; + } + + private boolean isMinSizeTeams() { + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + LASTeamStatsComponent teamStats = gameEntity.getComponent(LASTeamStatsComponent.class); int minTeamSize = gameEntity.getComponent(LASConfigComponent.class).minTeamSize; - String teleporterTeam = teleporter.getComponent(LASTeamComponent.class).team; - Iterable characters = entityManager.getEntitiesWith(PlayerCharacterComponent.class, - LASTeamComponent.class); - - for (EntityRef character : characters) { - String otherPlayerTeam = character.getComponent(LASTeamComponent.class).team; - if (teleporterTeam.equals(otherPlayerTeam)) { - teleporterTeamCount++; - } else if (!otherPlayerTeam.equals(LASUtils.WHITE_TEAM)) { - oppositeTeamCount++; - } - } - if (teleporterTeamCount - oppositeTeamCount < maxTeamSizeDifference) { - if (teleporterTeamCount >= minTeamSize - 1 && oppositeTeamCount >= minTeamSize && !gameStart) { - sendEventToClients(TimerEvent::new); - player.send(new DelayedDeactivateBarrierEvent(30000)); - gameStart = true; - } - return true; - } else { - if (maxTeamSizeDifference == 1) { - player.getOwner().send(new ChatMessageEvent( - "The " + teleporterTeam + " team has " + maxTeamSizeDifference + " player more, " + - "so please join the " + LASUtils.getOppositionTeam(teleporterTeam) + " team.", EntityRef.NULL)); - } else { - player.getOwner().send(new ChatMessageEvent( - "The " + teleporterTeam + " team has " + maxTeamSizeDifference + " players more, " + - "so please join the " + LASUtils.getOppositionTeam(teleporterTeam) + " team.", EntityRef.NULL)); - } - return false; - } + + return teamStats.redTeamSize >= minTeamSize && teamStats.blackTeamSize >= minTeamSize; } private String setPlayerTeamToTeleporterTeam(EntityRef player, EntityRef teleporter) { From d93de23f081003c81777bb23ba63b6b9397c933e Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Wed, 31 Aug 2022 23:09:59 +0200 Subject: [PATCH 21/51] refactor: replace gameStart bool with phase check and transition --- .../module/lightandshadow/systems/TeleporterSystem.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index 43667978..e5d49f4e 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -64,8 +64,6 @@ public class TeleporterSystem extends BaseComponentSystem { Optional prefab = Assets.getPrefab("inventory"); StartingInventoryComponent startingInventory = prefab.get().getComponent(StartingInventoryComponent.class); - private boolean gameStart; - private final Random random = new Random(); @Command(shortDescription = "Set the maximum team size difference", helpText = "Set maxTeamSizeDifference", runOnServer = true, @@ -101,10 +99,10 @@ public void onTeleportActivation(ActivateEvent event, EntityRef entity) { handlePlayerTeleport(player, team); // check game start condition - if (isMinSizeTeams() && !gameStart) { + if (isMinSizeTeams() && phaseSystem.getCurrentPhase() == Phase.PRE_GAME) { sendEventToClients(TimerEvent::new); player.send(new DelayedDeactivateBarrierEvent(30000)); - gameStart = true; + gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.COUNTDOWN)); } } else { EntityRef gameEntity = gameEntitySystem.getGameEntity(); From 7f3514c7dd9da472e882362adf0047814d83171a Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Wed, 31 Aug 2022 23:18:15 +0200 Subject: [PATCH 22/51] feat: add command to set min team size - move commands to GameEntitySystem --- .../systems/GameEntitySystem.java | 22 +++++++++++++++++++ .../systems/TeleporterSystem.java | 10 --------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/GameEntitySystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/GameEntitySystem.java index c378dd0d..f7890308 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/GameEntitySystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/GameEntitySystem.java @@ -12,6 +12,10 @@ import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.logic.console.commandSystem.annotations.Command; +import org.terasology.engine.logic.console.commandSystem.annotations.CommandParam; +import org.terasology.engine.logic.console.commandSystem.annotations.Sender; +import org.terasology.engine.logic.permission.PermissionManager; import org.terasology.engine.logic.players.PlayerCharacterComponent; import org.terasology.engine.registry.Share; import org.terasology.lightandshadowresources.components.LASTeamComponent; @@ -67,5 +71,23 @@ public void updateTeamStats() { gameEntity.addOrSaveComponent(teamStats); } + + @Command(shortDescription = "Set the maximum team size difference", helpText = "Set maxTeamSizeDifference", runOnServer = true, + requiredPermission = PermissionManager.CHEAT_PERMISSION) + public String setMaxTeamSizeDifference(@Sender EntityRef client, @CommandParam("difference") int difference) { + LASConfigComponent lasconfig = gameEntity.getComponent(LASConfigComponent.class); + lasconfig.maxTeamSizeDifference = difference; + gameEntity.saveComponent(lasconfig); + return "The max team size difference is set to " + difference; + } + + @Command(shortDescription = "Set the minimum team size", helpText = "Set minTeamSize", runOnServer = true, + requiredPermission = PermissionManager.CHEAT_PERMISSION) + public String setMinTeamSize(@Sender EntityRef client, @CommandParam("size") int size) { + LASConfigComponent lasconfig = gameEntity.getComponent(LASConfigComponent.class); + lasconfig.minTeamSize = size; + gameEntity.saveComponent(lasconfig); + return "The min team size is set to " + size; + } } diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index e5d49f4e..11adfe0a 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -66,16 +66,6 @@ public class TeleporterSystem extends BaseComponentSystem { private final Random random = new Random(); - @Command(shortDescription = "Set the maximum team size difference", helpText = "Set maxTeamSizeDifference", runOnServer = true, - requiredPermission = PermissionManager.CHEAT_PERMISSION) - public String setMaxTeamSizeDifference(@Sender EntityRef client, @CommandParam("difference") int difference) { - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - LASConfigComponent lasconfig = gameEntity.getComponent(LASConfigComponent.class); - lasconfig.maxTeamSizeDifference = difference; - gameEntity.saveComponent(lasconfig); - return "The max team size difference is set to " + difference; - } - /** * Depending on which teleporter the player chooses, they are set to that team * and teleported to that base From 75f22746de069f6b56a812eebfc884498e9cd5c7 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Wed, 31 Aug 2022 23:29:17 +0200 Subject: [PATCH 23/51] refactor: pregame client message - use configurable minTeamSize instead of static value - use broadcast OnPreGamePhaseStartedEvent instead of GameStartMesssage event --- .../events/GameStartMessageEvent.java | 13 ----------- .../systems/ClientPregameSystem.java | 13 +++++++---- .../systems/TeleporterSystem.java | 23 +++++++------------ 3 files changed, 16 insertions(+), 33 deletions(-) delete mode 100644 src/main/java/org/terasology/module/lightandshadow/events/GameStartMessageEvent.java diff --git a/src/main/java/org/terasology/module/lightandshadow/events/GameStartMessageEvent.java b/src/main/java/org/terasology/module/lightandshadow/events/GameStartMessageEvent.java deleted file mode 100644 index ef543514..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/events/GameStartMessageEvent.java +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.events; - -import org.terasology.engine.network.OwnerEvent; -import org.terasology.gestalt.entitysystem.event.Event; - -/** - * Notification event that indicates the pregame phase has started so that the pregame message can be displayed. - */ -@OwnerEvent -public class GameStartMessageEvent implements Event { -} diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java index 84d216f6..a37e5085 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java @@ -24,8 +24,9 @@ import org.terasology.input.ButtonState; import org.terasology.input.Input; import org.terasology.module.inventory.input.InventoryButton; -import org.terasology.module.lightandshadow.events.GameStartMessageEvent; +import org.terasology.module.lightandshadow.components.LASConfigComponent; import org.terasology.module.lightandshadow.events.TimerEvent; +import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; import org.terasology.notifications.events.ExpireNotificationEvent; import org.terasology.notifications.events.ShowNotificationEvent; import org.terasology.notifications.model.Notification; @@ -42,8 +43,6 @@ public class ClientPregameSystem extends BaseComponentSystem { public static final ResourceUrn ASSET_URI = new ResourceUrn("LightAndShadow:Timer"); - private static final String PREGAME_MESSAGE = "The game start's as soon as there is \n at least one player in each team."; - private static final int COUNTDOWN_IN_SECONDS = 30; private static Timer timer; @@ -55,6 +54,8 @@ public class ClientPregameSystem extends BaseComponentSystem { private EntityManager entityManager; @In private NUIManager nuiManager; + @In + private GameEntitySystem gameEntitySystem; @In private LocalPlayer localPlayer; @@ -72,9 +73,11 @@ public void shutdown() { } @ReceiveEvent - public void onPregameStart(GameStartMessageEvent event, EntityRef entity) { + public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { if (localPlayer.getClientEntity().equals(entity)) { - window.addNotification(PREGAME_MESSAGE); + String msg = "The game starts as soon as each team \n has at least " + + gameEntitySystem.getGameEntity().getComponent(LASConfigComponent.class).minTeamSize + " player(s)"; + window.addNotification(msg); entity.upsertComponent(AllowShopScreenComponent.class, c -> c.orElse(new AllowShopScreenComponent())); } } diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index 11adfe0a..615a9b9b 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -15,14 +15,8 @@ import org.terasology.engine.logic.characters.CharacterTeleportEvent; import org.terasology.engine.logic.chat.ChatMessageEvent; import org.terasology.engine.logic.common.ActivateEvent; -import org.terasology.engine.logic.console.commandSystem.annotations.Command; -import org.terasology.engine.logic.console.commandSystem.annotations.CommandParam; -import org.terasology.engine.logic.console.commandSystem.annotations.Sender; -import org.terasology.engine.logic.permission.PermissionManager; -import org.terasology.engine.logic.players.PlayerCharacterComponent; import org.terasology.engine.logic.players.SetDirectionEvent; import org.terasology.engine.network.ClientComponent; -import org.terasology.engine.network.events.DisconnectedEvent; import org.terasology.engine.registry.In; import org.terasology.engine.utilities.Assets; import org.terasology.gestalt.entitysystem.event.Event; @@ -35,7 +29,6 @@ import org.terasology.module.lightandshadow.components.LASConfigComponent; import org.terasology.module.lightandshadow.components.LASTeamStatsComponent; import org.terasology.module.lightandshadow.events.DelayedDeactivateBarrierEvent; -import org.terasology.module.lightandshadow.events.GameStartMessageEvent; import org.terasology.module.lightandshadow.events.TimerEvent; import org.terasology.module.lightandshadow.phases.Phase; import org.terasology.module.lightandshadow.phases.SwitchToPhaseEvent; @@ -87,13 +80,6 @@ public void onTeleportActivation(ActivateEvent event, EntityRef entity) { if (isBalancedTeams(targetTeam)) { String team = setPlayerTeamToTeleporterTeam(player, entity); handlePlayerTeleport(player, team); - - // check game start condition - if (isMinSizeTeams() && phaseSystem.getCurrentPhase() == Phase.PRE_GAME) { - sendEventToClients(TimerEvent::new); - player.send(new DelayedDeactivateBarrierEvent(30000)); - gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.COUNTDOWN)); - } } else { EntityRef gameEntity = gameEntitySystem.getGameEntity(); LASConfigComponent config = gameEntity.getComponent(LASConfigComponent.class); @@ -135,13 +121,20 @@ private void handlePlayerTeleport(EntityRef player, String team) { if (phaseSystem.getCurrentPhase() == Phase.IDLE) { gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.PRE_GAME)); } + Vector3f randomVector = new Vector3f(-1 + random.nextInt(3), 0, -1 + random.nextInt(3)); player.send(new CharacterTeleportEvent(randomVector.add(LASUtils.getTeleportDestination(team)))); player.send(new SetDirectionEvent(LASUtils.getYaw(LASUtils.getTeleportDestination(team). sub(LASUtils.getTeleportDestination(LASUtils.getOppositionTeam(team)), new Vector3f())), 0)); player.addOrSaveComponent(startingInventory); player.send(new RequestInventoryEvent(startingInventory.items)); - sendEventToClients(GameStartMessageEvent::new); + + // check game start condition + if (isMinSizeTeams() && phaseSystem.getCurrentPhase() == Phase.PRE_GAME) { + sendEventToClients(TimerEvent::new); + player.send(new DelayedDeactivateBarrierEvent(30000)); + gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.COUNTDOWN)); + } } private void sendEventToClients(Supplier eventSupplier) { From 840dc480e22057fd1fac36d78b420a97f883786b Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Wed, 31 Aug 2022 23:42:05 +0200 Subject: [PATCH 24/51] feat: replace client pregame screen message with notification --- .../systems/ClientPregameSystem.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java index a37e5085..dc930638 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java @@ -39,7 +39,8 @@ @RegisterSystem(RegisterMode.CLIENT) public class ClientPregameSystem extends BaseComponentSystem { - private static final String NOTIFICATION_ID = "LightAndShadow:firstTimeShop"; + private static final String SHOP_NOTIFICATION_ID = "LightAndShadow:firstTimeShop"; + private static final String WAIT_NOTIFICATION_ID = "LightAndShadow:waitForPlayers"; public static final ResourceUrn ASSET_URI = new ResourceUrn("LightAndShadow:Timer"); @@ -75,9 +76,12 @@ public void shutdown() { @ReceiveEvent public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { if (localPlayer.getClientEntity().equals(entity)) { - String msg = "The game starts as soon as each team \n has at least " + - gameEntitySystem.getGameEntity().getComponent(LASConfigComponent.class).minTeamSize + " player(s)"; - window.addNotification(msg); + Notification notification = new Notification(SHOP_NOTIFICATION_ID, + "Ain't Nobody Got Time For That!", + "The game starts as soon as each team has at least " + + gameEntitySystem.getGameEntity().getComponent(LASConfigComponent.class).minTeamSize + " player(s)", + "engine:icons#halfGreenHeart"); + localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); entity.upsertComponent(AllowShopScreenComponent.class, c -> c.orElse(new AllowShopScreenComponent())); } } @@ -91,11 +95,11 @@ public void updateTimer(TimerEvent event, EntityRef entity) { boolean addNotification; public void run() { if (addNotification) { - window.removeNotification(PREGAME_MESSAGE); + entity.send(new ExpireNotificationEvent(WAIT_NOTIFICATION_ID)); window.addNotification("The game starts in " + timePeriod-- + " seconds."); addNotification = false; } else { - window.removeNotification(PREGAME_MESSAGE); + entity.send(new ExpireNotificationEvent(WAIT_NOTIFICATION_ID)); window.removeNotification("The game starts in " + (timePeriod + 1) + " seconds."); addNotification = true; } @@ -120,7 +124,7 @@ public void run() { @ReceiveEvent(components = {ClientComponent.class, AllowShopScreenComponent.class}) public void onInGameShopButton(InventoryButton event, EntityRef entity) { if (event.getState() == ButtonState.DOWN) { - entity.send(new ExpireNotificationEvent(NOTIFICATION_ID)); + entity.send(new ExpireNotificationEvent(SHOP_NOTIFICATION_ID)); } } @@ -156,7 +160,7 @@ private String getActivationKey(SimpleUri button) { @ReceiveEvent(components = AllowShopScreenComponent.class) public void onShopComponentAdded(OnAddedComponent event, EntityRef entity) { - Notification notification = new Notification(NOTIFICATION_ID, + Notification notification = new Notification(SHOP_NOTIFICATION_ID, "Shut Up and Take My Money!", "Press " + getActivationKey(new SimpleUri("Inventory:inventory")) + " to buy items", "Economy:GoldCoin"); From 67a0ea98726f2b50999857c0e49853291c6c2ccc Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Thu, 1 Sep 2022 00:24:50 +0200 Subject: [PATCH 25/51] chore: reduce notification text lengths --- .../systems/ClientPregameSystem.java | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java index dc930638..a9dab7a8 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java @@ -75,15 +75,13 @@ public void shutdown() { @ReceiveEvent public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { - if (localPlayer.getClientEntity().equals(entity)) { - Notification notification = new Notification(SHOP_NOTIFICATION_ID, - "Ain't Nobody Got Time For That!", - "The game starts as soon as each team has at least " + - gameEntitySystem.getGameEntity().getComponent(LASConfigComponent.class).minTeamSize + " player(s)", - "engine:icons#halfGreenHeart"); - localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); - entity.upsertComponent(AllowShopScreenComponent.class, c -> c.orElse(new AllowShopScreenComponent())); - } + Notification notification = new Notification(SHOP_NOTIFICATION_ID, + "Too Few Players", + "Each team needs at least " + + gameEntitySystem.getGameEntity().getComponent(LASConfigComponent.class).minTeamSize + " player(s)", + "engine:icons#halfGreenHeart"); + localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); + entity.upsertComponent(AllowShopScreenComponent.class, c -> c.orElse(new AllowShopScreenComponent())); } @ReceiveEvent From 8e48b4d99cbae2f5803302adaab7102666c59270 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Thu, 22 Sep 2022 22:51:16 +0200 Subject: [PATCH 26/51] fix: Broadcast event not broadcasting due to missing Network component --- assets/prefabs/gameEntity.prefab | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/prefabs/gameEntity.prefab b/assets/prefabs/gameEntity.prefab index d1be04e6..0479eb5f 100644 --- a/assets/prefabs/gameEntity.prefab +++ b/assets/prefabs/gameEntity.prefab @@ -5,5 +5,9 @@ }, "CurrencyStorage": { "amount": 100 + }, + // this component is required for the PhaseSystem phase change events to be broadcasted + "Network": { + "replicateMode": "ALWAYS" } } From 84bf592bccb48fa05862ad0a205e2084e28d7c58 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Thu, 22 Sep 2022 22:51:48 +0200 Subject: [PATCH 27/51] chore: remove unnecessary imports --- .../phases/authority/PhaseSystem.java | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java index a4f46b22..592cf0f2 100644 --- a/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/phases/authority/PhaseSystem.java @@ -9,27 +9,16 @@ import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterMode; import org.terasology.engine.entitySystem.systems.RegisterSystem; -import org.terasology.engine.logic.common.ActivateEvent; import org.terasology.engine.registry.In; import org.terasology.engine.registry.Share; import org.terasology.gestalt.entitysystem.event.ReceiveEvent; -import org.terasology.lightandshadowresources.components.SetTeamOnActivateComponent; -import org.terasology.module.lightandshadow.phases.OnCountdownPhaseEndedEvent; -import org.terasology.module.lightandshadow.phases.OnCountdownPhaseStartedEvent; import org.terasology.module.lightandshadow.phases.OnIdlePhaseEndedEvent; -import org.terasology.module.lightandshadow.phases.OnIdlePhaseStartedEvent; -import org.terasology.module.lightandshadow.phases.OnInGamePhaseEndedEvent; -import org.terasology.module.lightandshadow.phases.OnInGamePhaseStartedEvent; -import org.terasology.module.lightandshadow.phases.OnPostGamePhaseEndedEvent; -import org.terasology.module.lightandshadow.phases.OnPostGamePhaseStartedEvent; -import org.terasology.module.lightandshadow.phases.OnPreGamePhaseEndedEvent; -import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; import org.terasology.module.lightandshadow.phases.Phase; import org.terasology.module.lightandshadow.phases.SwitchToPhaseEvent; import org.terasology.module.lightandshadow.systems.GameEntitySystem; /** - * Provides an entity that keeps track of game state information. + * Provides an entity that keeps track of game state information. */ @RegisterSystem(RegisterMode.AUTHORITY) @Share(value = PhaseSystem.class) @@ -57,7 +46,7 @@ void transitionPhase(Phase from, Phase to) { logger.debug("Transitioning phases from " + from + " to " + to); endPhase(from); startPhase(to); - }; + } @ReceiveEvent(components = PhaseComponent.class) public void switchToPhase(SwitchToPhaseEvent event, EntityRef entity) { From 447122f2cd83e59082233a083de9b17d4649febd Mon Sep 17 00:00:00 2001 From: "Josephine \"Niruandaleth\" Rueckert" Date: Fri, 23 Sep 2022 11:23:15 +0200 Subject: [PATCH 28/51] refactor: move duplicated notification helper into LASUtils --- .../module/lightandshadow/LASUtils.java | 37 +++++++++++++++++++ .../systems/ClientPregameSystem.java | 37 +------------------ .../systems/ClientStatisticsSystem.java | 36 +----------------- 3 files changed, 40 insertions(+), 70 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/LASUtils.java b/src/main/java/org/terasology/module/lightandshadow/LASUtils.java index 5affd5bd..1dc5a09d 100644 --- a/src/main/java/org/terasology/module/lightandshadow/LASUtils.java +++ b/src/main/java/org/terasology/module/lightandshadow/LASUtils.java @@ -13,6 +13,13 @@ import java.util.function.Supplier; import java.util.stream.Stream; +import org.terasology.engine.core.SimpleUri; +import org.terasology.engine.unicode.EnclosedAlphanumerics; +import org.terasology.engine.input.InputSystem; +import org.terasology.input.Input; +import org.terasology.nui.Color; +import org.terasology.nui.FontColor; + import static java.util.Collections.singletonMap; public final class LASUtils { @@ -222,4 +229,34 @@ public static String pixel(int x, int y, int r) { .findAny() .orElse("engine:air"); } + + /** + * Get a formatted representation of the primary {@link Input} associated with the given button binding. + * + * If the display name of the primary bound key is a single character this representation will be the encircled + * character. Otherwise the full display name is used. The bound key will be printed in yellow. + * + * If no key binding was found the text "n/a" in red color is returned. + * + * @param button the URI of a bindable button + * @return a formatted text to be used as representation for the player + */ + //TODO: put this in a common place? Duplicated in Dialogs, EventualSkills, and InGameHelp + public static String getActivationKey(InputSystem inputSystem, SimpleUri button) { + return inputSystem.getInputsForBindButton(button).stream() + .findFirst() + .map(Input::getDisplayName) + .map(key -> { + if (key.length() == 1) { + // print the key in yellow within a circle + int off = key.charAt(0) - 'A'; + char code = (char) (EnclosedAlphanumerics.CIRCLED_LATIN_CAPITAL_LETTER_A + off); + return String.valueOf(code); + } else { + return key; + } + }) + .map(key -> FontColor.getColored(key, Color.yellow)) + .orElse(FontColor.getColored("n/a", Color.red)); + } } diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java index a9dab7a8..058b2d16 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java @@ -18,12 +18,11 @@ import org.terasology.engine.network.ClientComponent; import org.terasology.engine.registry.In; import org.terasology.engine.rendering.nui.NUIManager; -import org.terasology.engine.unicode.EnclosedAlphanumerics; import org.terasology.gestalt.assets.ResourceUrn; import org.terasology.gestalt.entitysystem.event.ReceiveEvent; import org.terasology.input.ButtonState; -import org.terasology.input.Input; import org.terasology.module.inventory.input.InventoryButton; +import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.components.LASConfigComponent; import org.terasology.module.lightandshadow.events.TimerEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; @@ -31,8 +30,6 @@ import org.terasology.notifications.events.ShowNotificationEvent; import org.terasology.notifications.model.Notification; import org.terasology.notify.ui.DialogNotificationOverlay; -import org.terasology.nui.Color; -import org.terasology.nui.FontColor; import java.util.Timer; import java.util.TimerTask; @@ -126,41 +123,11 @@ public void onInGameShopButton(InventoryButton event, EntityRef entity) { } } - /** - * Get a formatted representation of the primary {@link Input} associated with the given button binding. - * - * If the display name of the primary bound key is a single character this representation will be the encircled - * character. Otherwise the full display name is used. The bound key will be printed in yellow. - * - * If no key binding was found the text "n/a" in red color is returned. - * - * @param button the URI of a bindable button - * @return a formatted text to be used as representation for the player - */ - //TODO: put this in a common place? Duplicated in Dialogs, EventualSkills, and InGameHelp - private String getActivationKey(SimpleUri button) { - return inputSystem.getInputsForBindButton(button).stream() - .findFirst() - .map(Input::getDisplayName) - .map(key -> { - if (key.length() == 1) { - // print the key in yellow within a circle - int off = key.charAt(0) - 'A'; - char code = (char) (EnclosedAlphanumerics.CIRCLED_LATIN_CAPITAL_LETTER_A + off); - return String.valueOf(code); - } else { - return key; - } - }) - .map(key -> FontColor.getColored(key, Color.yellow)) - .orElse(FontColor.getColored("n/a", Color.red)); - } - @ReceiveEvent(components = AllowShopScreenComponent.class) public void onShopComponentAdded(OnAddedComponent event, EntityRef entity) { Notification notification = new Notification(SHOP_NOTIFICATION_ID, "Shut Up and Take My Money!", - "Press " + getActivationKey(new SimpleUri("Inventory:inventory")) + " to buy items", + "Press " + LASUtils.getActivationKey(inputSystem, new SimpleUri("Inventory:inventory")) + " to buy items", "Economy:GoldCoin"); localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); } diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientStatisticsSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientStatisticsSystem.java index 3c5758f0..c103f612 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientStatisticsSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientStatisticsSystem.java @@ -15,17 +15,13 @@ import org.terasology.engine.registry.In; import org.terasology.engine.rendering.nui.NUIManager; import org.terasology.engine.rendering.nui.layers.ingame.DeathScreen; -import org.terasology.engine.unicode.EnclosedAlphanumerics; import org.terasology.gestalt.entitysystem.event.ReceiveEvent; -import org.terasology.input.Input; import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.events.ScoreUpdateFromServerEvent; import org.terasology.module.lightandshadow.TapButton; import org.terasology.notifications.events.ExpireNotificationEvent; import org.terasology.notifications.events.ShowNotificationEvent; import org.terasology.notifications.model.Notification; -import org.terasology.nui.Color; -import org.terasology.nui.FontColor; /** * Displays statistics screen when required. @@ -90,41 +86,11 @@ public void onTab(TapButton event, EntityRef entity) { } } - /** - * Get a formatted representation of the primary {@link Input} associated with the given button binding. - * - * If the display name of the primary bound key is a single character this representation will be the encircled - * character. Otherwise the full display name is used. The bound key will be printed in yellow. - * - * If no key binding was found the text "n/a" in red color is returned. - * - * @param button the URI of a bindable button - * @return a formatted text to be used as representation for the player - */ - //TODO: put this in a common place? Duplicated in Dialogs, EventualSkills, and InGameHelp - private String getActivationKey(SimpleUri button) { - return inputSystem.getInputsForBindButton(button).stream() - .findFirst() - .map(Input::getDisplayName) - .map(key -> { - if (key.length() == 1) { - // print the key in yellow within a circle - int off = key.charAt(0) - 'A'; - char code = (char) (EnclosedAlphanumerics.CIRCLED_LATIN_CAPITAL_LETTER_A + off); - return String.valueOf(code); - } else { - return key; - } - }) - .map(key -> FontColor.getColored(key, Color.yellow)) - .orElse(FontColor.getColored("n/a", Color.red)); - } - @ReceiveEvent public void onLocalPlayerInitialized(LocalPlayerInitializedEvent event, EntityRef entity) { Notification notification = new Notification(NOTIFICATION_ID, "The Numbers Game", - "Hold " + getActivationKey(new SimpleUri("LightAndShadow:statistics")) + " to see statistics", + "Hold " + LASUtils.getActivationKey(inputSystem, new SimpleUri("LightAndShadow:statistics")) + " to see statistics", "engine:items#blueBook"); localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); } From c2a7cf1643740caaf978053cdb93e19f225b1d37 Mon Sep 17 00:00:00 2001 From: "Josephine \"Niruandaleth\" Rueckert" Date: Fri, 23 Sep 2022 11:24:27 +0200 Subject: [PATCH 29/51] chore: update team stats directly before stat-based checks --- .../module/lightandshadow/systems/TeleporterSystem.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index 615a9b9b..3dc1a049 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -75,8 +75,6 @@ public void onTeleportActivation(ActivateEvent event, EntityRef entity) { logger.debug("Player {} attempted to join white team", player.getId()); event.consume(); } else { - gameEntitySystem.updateTeamStats(); - if (isBalancedTeams(targetTeam)) { String team = setPlayerTeamToTeleporterTeam(player, entity); handlePlayerTeleport(player, team); @@ -91,6 +89,7 @@ public void onTeleportActivation(ActivateEvent event, EntityRef entity) { } private boolean isBalancedTeams(String targetTeam) { + gameEntitySystem.updateTeamStats(); EntityRef gameEntity = gameEntitySystem.getGameEntity(); LASTeamStatsComponent teamStats = gameEntity.getComponent(LASTeamStatsComponent.class); int maxTeamSizeDifference = gameEntity.getComponent(LASConfigComponent.class).maxTeamSizeDifference; @@ -101,6 +100,7 @@ private boolean isBalancedTeams(String targetTeam) { } private boolean isMinSizeTeams() { + gameEntitySystem.updateTeamStats(); EntityRef gameEntity = gameEntitySystem.getGameEntity(); LASTeamStatsComponent teamStats = gameEntity.getComponent(LASTeamStatsComponent.class); int minTeamSize = gameEntity.getComponent(LASConfigComponent.class).minTeamSize; From 4f839dfeee60aff860d0ba408843ddc6b1b85adf Mon Sep 17 00:00:00 2001 From: "Josephine \"Niruandaleth\" Rueckert" Date: Fri, 23 Sep 2022 11:26:56 +0200 Subject: [PATCH 30/51] feat: show notification for new players and expire it on phase end --- .../systems/ClientPregameSystem.java | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java index 058b2d16..55219324 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java @@ -15,6 +15,7 @@ import org.terasology.engine.entitySystem.systems.RegisterSystem; import org.terasology.engine.input.InputSystem; import org.terasology.engine.logic.players.LocalPlayer; +import org.terasology.engine.logic.players.event.OnPlayerSpawnedEvent; import org.terasology.engine.network.ClientComponent; import org.terasology.engine.registry.In; import org.terasology.engine.rendering.nui.NUIManager; @@ -26,6 +27,9 @@ import org.terasology.module.lightandshadow.components.LASConfigComponent; import org.terasology.module.lightandshadow.events.TimerEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; +import org.terasology.module.lightandshadow.phases.OnPreGamePhaseEndedEvent; +import org.terasology.module.lightandshadow.phases.Phase; +import org.terasology.module.lightandshadow.phases.authority.PhaseSystem; import org.terasology.notifications.events.ExpireNotificationEvent; import org.terasology.notifications.events.ShowNotificationEvent; import org.terasology.notifications.model.Notification; @@ -54,6 +58,8 @@ public class ClientPregameSystem extends BaseComponentSystem { private NUIManager nuiManager; @In private GameEntitySystem gameEntitySystem; + @In + private PhaseSystem phaseSystem; @In private LocalPlayer localPlayer; @@ -72,15 +78,22 @@ public void shutdown() { @ReceiveEvent public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { - Notification notification = new Notification(SHOP_NOTIFICATION_ID, - "Too Few Players", - "Each team needs at least " + - gameEntitySystem.getGameEntity().getComponent(LASConfigComponent.class).minTeamSize + " player(s)", - "engine:icons#halfGreenHeart"); - localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); + notifyOnTooFewPlayers(); entity.upsertComponent(AllowShopScreenComponent.class, c -> c.orElse(new AllowShopScreenComponent())); } + @ReceiveEvent + public void onSpawnDuringPregame(OnPlayerSpawnedEvent event, EntityRef entity) { + if (phaseSystem.getCurrentPhase() == Phase.PRE_GAME) { + notifyOnTooFewPlayers(); + } + } + + @ReceiveEvent + public void onPregameEnd(OnPreGamePhaseEndedEvent event, EntityRef entity) { + localPlayer.getClientEntity().send(new ExpireNotificationEvent(WAIT_NOTIFICATION_ID)); + } + @ReceiveEvent public void updateTimer(TimerEvent event, EntityRef entity) { if (localPlayer.getClientEntity().equals(entity)) { @@ -131,4 +144,13 @@ public void onShopComponentAdded(OnAddedComponent event, EntityRef entity) { "Economy:GoldCoin"); localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); } + + public void notifyOnTooFewPlayers() { + Notification notification = new Notification(WAIT_NOTIFICATION_ID, + "Too Few Players", + "Each team needs at least " + + gameEntitySystem.getGameEntity().getComponent(LASConfigComponent.class).minTeamSize + " player(s)", + "engine:icons#halfGreenHeart"); + localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); + } } From 142786428f607b8b5990840bc4f5cf8e28528f2c Mon Sep 17 00:00:00 2001 From: "Josephine \"Niruandaleth\" Rueckert" Date: Sun, 25 Sep 2022 10:02:12 +0200 Subject: [PATCH 31/51] refactor: extract client shop logic into new ClientShopSystem --- .../systems/ClientPregameSystem.java | 29 -------- .../systems/ClientShopSystem.java | 70 +++++++++++++++++++ 2 files changed, 70 insertions(+), 29 deletions(-) create mode 100644 src/main/java/org/terasology/module/lightandshadow/systems/ClientShopSystem.java diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java index 55219324..22e779b5 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java @@ -2,8 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 package org.terasology.module.lightandshadow.systems; -import org.terasology.economy.components.AllowShopScreenComponent; -import org.terasology.economy.ui.MarketUiClientSystem; import org.terasology.engine.core.SimpleUri; import org.terasology.engine.entitySystem.entity.EntityManager; import org.terasology.engine.entitySystem.entity.EntityRef; @@ -40,7 +38,6 @@ @RegisterSystem(RegisterMode.CLIENT) public class ClientPregameSystem extends BaseComponentSystem { - private static final String SHOP_NOTIFICATION_ID = "LightAndShadow:firstTimeShop"; private static final String WAIT_NOTIFICATION_ID = "LightAndShadow:waitForPlayers"; public static final ResourceUrn ASSET_URI = new ResourceUrn("LightAndShadow:Timer"); @@ -79,7 +76,6 @@ public void shutdown() { @ReceiveEvent public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { notifyOnTooFewPlayers(); - entity.upsertComponent(AllowShopScreenComponent.class, c -> c.orElse(new AllowShopScreenComponent())); } @ReceiveEvent @@ -120,31 +116,6 @@ public void run() { } } - /** - * Handles the button event if in-game shop is enabled. - * Needs to have a higher priority than {@link MarketUiClientSystem#onToggleInventory(InventoryButton, EntityRef)} - * to receive the {@link InventoryButton} event before it is consumed. - * - * @param event the help button event. - * @param entity the entity to display the help screen to. - */ - @Priority(EventPriority.PRIORITY_CRITICAL) - @ReceiveEvent(components = {ClientComponent.class, AllowShopScreenComponent.class}) - public void onInGameShopButton(InventoryButton event, EntityRef entity) { - if (event.getState() == ButtonState.DOWN) { - entity.send(new ExpireNotificationEvent(SHOP_NOTIFICATION_ID)); - } - } - - @ReceiveEvent(components = AllowShopScreenComponent.class) - public void onShopComponentAdded(OnAddedComponent event, EntityRef entity) { - Notification notification = new Notification(SHOP_NOTIFICATION_ID, - "Shut Up and Take My Money!", - "Press " + LASUtils.getActivationKey(inputSystem, new SimpleUri("Inventory:inventory")) + " to buy items", - "Economy:GoldCoin"); - localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); - } - public void notifyOnTooFewPlayers() { Notification notification = new Notification(WAIT_NOTIFICATION_ID, "Too Few Players", diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientShopSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientShopSystem.java new file mode 100644 index 00000000..177fd928 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientShopSystem.java @@ -0,0 +1,70 @@ +// Copyright 2021 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 +package org.terasology.module.lightandshadow.systems; + +import org.terasology.economy.components.AllowShopScreenComponent; +import org.terasology.economy.ui.MarketUiClientSystem; +import org.terasology.engine.core.SimpleUri; +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.entity.lifecycleEvents.OnAddedComponent; +import org.terasology.engine.entitySystem.event.EventPriority; +import org.terasology.engine.entitySystem.event.Priority; +import org.terasology.engine.entitySystem.systems.BaseComponentSystem; +import org.terasology.engine.entitySystem.systems.RegisterMode; +import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.input.InputSystem; +import org.terasology.engine.logic.players.LocalPlayer; +import org.terasology.engine.network.ClientComponent; +import org.terasology.engine.registry.In; +import org.terasology.gestalt.entitysystem.event.ReceiveEvent; +import org.terasology.input.ButtonState; +import org.terasology.module.inventory.input.InventoryButton; +import org.terasology.module.lightandshadow.LASUtils; +import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; +import org.terasology.notifications.events.ExpireNotificationEvent; +import org.terasology.notifications.events.ShowNotificationEvent; +import org.terasology.notifications.model.Notification; + +import java.util.Timer; +import java.util.TimerTask; + +@RegisterSystem(RegisterMode.CLIENT) +public class ClientShopSystem extends BaseComponentSystem { + private static final String SHOP_NOTIFICATION_ID = "LightAndShadow:firstTimeShop"; + + @In + InputSystem inputSystem; + + @In + private LocalPlayer localPlayer; + + @ReceiveEvent + public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { + entity.upsertComponent(AllowShopScreenComponent.class, c -> c.orElse(new AllowShopScreenComponent())); + } + + /** + * Handles the button event if in-game shop is enabled. + * Needs to have a higher priority than {@link MarketUiClientSystem#onToggleInventory(InventoryButton, EntityRef)} + * to receive the {@link InventoryButton} event before it is consumed. + * + * @param event the help button event. + * @param entity the entity to display the help screen to. + */ + @Priority(EventPriority.PRIORITY_CRITICAL) + @ReceiveEvent(components = {ClientComponent.class, AllowShopScreenComponent.class}) + public void onInGameShopButton(InventoryButton event, EntityRef entity) { + if (event.getState() == ButtonState.DOWN) { + entity.send(new ExpireNotificationEvent(SHOP_NOTIFICATION_ID)); + } + } + + @ReceiveEvent(components = AllowShopScreenComponent.class) + public void onShopComponentAdded(OnAddedComponent event, EntityRef entity) { + Notification notification = new Notification(SHOP_NOTIFICATION_ID, + "Shut Up and Take My Money!", + "Press " + LASUtils.getActivationKey(inputSystem, new SimpleUri("Inventory:inventory")) + " to buy items", + "Economy:GoldCoin"); + localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); + } +} \ No newline at end of file From ecc6b87def0f344426e24aac6d0fa20c9f1f2f94 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Fri, 30 Sep 2022 23:51:50 +0200 Subject: [PATCH 32/51] refactor: rename ClientPregameSystem -> ClientGameStateInfoSystem --- ...egameSystem.java => ClientGameStateInfoSystem.java} | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) rename src/main/java/org/terasology/module/lightandshadow/systems/{ClientPregameSystem.java => ClientGameStateInfoSystem.java} (89%) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientGameStateInfoSystem.java similarity index 89% rename from src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java rename to src/main/java/org/terasology/module/lightandshadow/systems/ClientGameStateInfoSystem.java index 22e779b5..b2aa0417 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientPregameSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientGameStateInfoSystem.java @@ -2,26 +2,18 @@ // SPDX-License-Identifier: Apache-2.0 package org.terasology.module.lightandshadow.systems; -import org.terasology.engine.core.SimpleUri; import org.terasology.engine.entitySystem.entity.EntityManager; import org.terasology.engine.entitySystem.entity.EntityRef; -import org.terasology.engine.entitySystem.entity.lifecycleEvents.OnAddedComponent; -import org.terasology.engine.entitySystem.event.EventPriority; -import org.terasology.engine.entitySystem.event.Priority; import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterMode; import org.terasology.engine.entitySystem.systems.RegisterSystem; import org.terasology.engine.input.InputSystem; import org.terasology.engine.logic.players.LocalPlayer; import org.terasology.engine.logic.players.event.OnPlayerSpawnedEvent; -import org.terasology.engine.network.ClientComponent; import org.terasology.engine.registry.In; import org.terasology.engine.rendering.nui.NUIManager; import org.terasology.gestalt.assets.ResourceUrn; import org.terasology.gestalt.entitysystem.event.ReceiveEvent; -import org.terasology.input.ButtonState; -import org.terasology.module.inventory.input.InventoryButton; -import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.components.LASConfigComponent; import org.terasology.module.lightandshadow.events.TimerEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; @@ -37,7 +29,7 @@ import java.util.TimerTask; @RegisterSystem(RegisterMode.CLIENT) -public class ClientPregameSystem extends BaseComponentSystem { +public class ClientGameStateInfoSystem extends BaseComponentSystem { private static final String WAIT_NOTIFICATION_ID = "LightAndShadow:waitForPlayers"; public static final ResourceUrn ASSET_URI = new ResourceUrn("LightAndShadow:Timer"); From 2a39f5152c1865571413558d00fc81e696c341a9 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 2 Oct 2022 17:14:56 +0200 Subject: [PATCH 33/51] feat: restrict conditions in which barrier effect is applied - only in pre-game and countdown phases - only if player is neither spectator (white team) nor unassigned --- .../lightandshadow/systems/MagicDomeSystem.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java index aa166cfc..f0e1d922 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java @@ -23,6 +23,8 @@ import org.terasology.module.lightandshadow.components.MagicDome; import org.terasology.module.lightandshadow.events.DelayedDeactivateBarrierEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; +import org.terasology.module.lightandshadow.phases.Phase; +import org.terasology.module.lightandshadow.phases.authority.PhaseSystem; @RegisterSystem(RegisterMode.AUTHORITY) public class MagicDomeSystem extends BaseComponentSystem { @@ -31,6 +33,8 @@ public class MagicDomeSystem extends BaseComponentSystem { @In DelayManager delayManager; @In + PhaseSystem phaseSystem; + @In private EntityManager entityManager; private Vector3f position = new Vector3f(); @@ -69,8 +73,19 @@ public void deactivateBarriers(DelayedActionTriggeredEvent event, EntityRef enti } - @ReceiveEvent(components = LocationComponent.class) + @ReceiveEvent(components = {LocationComponent.class, LASTeamComponent.class}) public void onCharacterMovement(CharacterMoveInputEvent moveInputEvent, EntityRef player, LocationComponent loc) { + String team = player.getComponent(LASTeamComponent.class).team; + if (team.equals(LASUtils.WHITE_TEAM)) { + // spectators should not be affected by the barriers + return; + } + + if (! (phaseSystem.getCurrentPhase().equals(Phase.PRE_GAME) || phaseSystem.getCurrentPhase().equals(Phase.COUNTDOWN))) { + // barriers should only be active during pre-game and countdown phase + return; + } + Vector3f pos = new Vector3f(loc.getWorldPosition(new Vector3f())); for (EntityRef domeEntity : entityManager.getEntitiesWith(MagicDome.class, LocationComponent.class)) { From 75a4d42fbd7af4c6121ce44475e0e70d4d3e3366 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 2 Oct 2022 17:19:21 +0200 Subject: [PATCH 34/51] feat: add cmd to teleport back to platform --- .../systems/TeleporterSystem.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index 3dc1a049..6b73e5bd 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -15,6 +15,9 @@ import org.terasology.engine.logic.characters.CharacterTeleportEvent; import org.terasology.engine.logic.chat.ChatMessageEvent; import org.terasology.engine.logic.common.ActivateEvent; +import org.terasology.engine.logic.console.commandSystem.annotations.Command; +import org.terasology.engine.logic.console.commandSystem.annotations.Sender; +import org.terasology.engine.logic.permission.PermissionManager; import org.terasology.engine.logic.players.SetDirectionEvent; import org.terasology.engine.network.ClientComponent; import org.terasology.engine.registry.In; @@ -143,4 +146,18 @@ private void sendEventToClients(Supplier eventSupplier) { client.send(eventSupplier.get()); } } + + @Command(shortDescription = "Teleport player back to platform", helpText = "Platform Teleport", runOnServer = true, + requiredPermission = PermissionManager.CHEAT_PERMISSION) + public String teleportToPlatform(@Sender EntityRef sender) { + ClientComponent clientComp = sender.getComponent(ClientComponent.class); + LASTeamComponent senderTeam = clientComp.character.getComponent(LASTeamComponent.class); + if (senderTeam != null && (senderTeam.team.equals(LASUtils.RED_TEAM) || senderTeam.team.equals(LASUtils.BLACK_TEAM))) { + // players teleporting back to platform should be removed from the playing teams + // white team (spectator) members are allowed to teleport without losing their team + clientComp.character.removeComponent(LASTeamComponent.class); + } + clientComp.character.send(new CharacterTeleportEvent(new Vector3f(LASUtils.FLOATING_PLATFORM_POSITION).add(0, 1, 0))); + return "Teleporting you to the platform."; + } } From 238b6106e310b84afe4d97684cd4d875aa0bf22d Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 19:28:45 +0200 Subject: [PATCH 35/51] refactor: extract team-related helpers into new TeamSystem --- .../lightandshadow/systems/TeamSystem.java | 76 +++++++++++++++++++ .../systems/TeleporterSystem.java | 40 ++-------- 2 files changed, 84 insertions(+), 32 deletions(-) create mode 100644 src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java new file mode 100644 index 00000000..5d491abd --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java @@ -0,0 +1,76 @@ +// Copyright 2022 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.systems; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.entity.lifecycleEvents.OnChangedComponent; +import org.terasology.engine.entitySystem.systems.BaseComponentSystem; +import org.terasology.engine.entitySystem.systems.RegisterMode; +import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.network.ClientComponent; +import org.terasology.engine.registry.In; +import org.terasology.gestalt.entitysystem.event.ReceiveEvent; +import org.terasology.lightandshadowresources.components.LASTeamComponent; +import org.terasology.module.lightandshadow.LASUtils; +import org.terasology.module.lightandshadow.components.LASConfigComponent; +import org.terasology.module.lightandshadow.components.LASTeamStatsComponent; + +@RegisterSystem(RegisterMode.AUTHORITY) +public class TeamSystem extends BaseComponentSystem { + @In + GameEntitySystem gameEntitySystem; + + private static final Logger logger = LoggerFactory.getLogger(TeamSystem.class); + + @ReceiveEvent(components = LASTeamComponent.class) + public void onTeamChange(OnChangedComponent event, EntityRef entity) { + logger.debug("Player {} switched to team {}", + entity.getComponent(ClientComponent.class).clientInfo, + entity.getComponent(LASTeamComponent.class).team); + } + + public boolean isBalancedTeams(String targetTeam) { + gameEntitySystem.updateTeamStats(); + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + LASTeamStatsComponent teamStats = gameEntity.getComponent(LASTeamStatsComponent.class); + int maxTeamSizeDifference = gameEntity.getComponent(LASConfigComponent.class).maxTeamSizeDifference; + int currentTeamSizeDiff = teamStats.blackTeamSize - teamStats.redTeamSize; + int postTeleportTeamSizeDiff = targetTeam.equals(LASUtils.BLACK_TEAM) ? currentTeamSizeDiff + 1 : currentTeamSizeDiff - 1; + + return Math.abs(postTeleportTeamSizeDiff) <= maxTeamSizeDifference; + } + + public boolean isMinSizeTeams() { + gameEntitySystem.updateTeamStats(); + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + LASTeamStatsComponent teamStats = gameEntity.getComponent(LASTeamStatsComponent.class); + int minTeamSize = gameEntity.getComponent(LASConfigComponent.class).minTeamSize; + + return teamStats.redTeamSize >= minTeamSize && teamStats.blackTeamSize >= minTeamSize; + } + + public String setPlayerTeamToTeam(EntityRef player, String team) { + LASTeamComponent playerTeamComponent = player.getComponent(LASTeamComponent.class); + playerTeamComponent.team = team; + player.saveComponent(playerTeamComponent); + return playerTeamComponent.team; + } + + public int getTeamSize(String team) { + gameEntitySystem.updateTeamStats(); + EntityRef gameEntity = gameEntitySystem.getGameEntity(); + LASTeamStatsComponent teamStats = gameEntity.getComponent(LASTeamStatsComponent.class); + switch (team) { + case LASUtils.BLACK_TEAM: + return teamStats.blackTeamSize; + case LASUtils.RED_TEAM: + return teamStats.redTeamSize; + case LASUtils.WHITE_TEAM: + return teamStats.whiteTeamSize; + } + } + +} diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index 6b73e5bd..ee8dfd39 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -54,6 +54,8 @@ public class TeleporterSystem extends BaseComponentSystem { GameEntitySystem gameEntitySystem; @In PhaseSystem phaseSystem; + @In + TeamSystem teamSystem; private static final Logger logger = LoggerFactory.getLogger(TeleporterSystem.class); @@ -65,6 +67,7 @@ public class TeleporterSystem extends BaseComponentSystem { /** * Depending on which teleporter the player chooses, they are set to that team * and teleported to that base + * Assumption: there are two teleporters, one for the red, one for the black team * * @param event * @param entity @@ -75,11 +78,12 @@ public void onTeleportActivation(ActivateEvent event, EntityRef entity) { String targetTeam = entity.getComponent(LASTeamComponent.class).team; if (targetTeam.equals(LASUtils.WHITE_TEAM)) { // teleporters are (currently) expected to only target black or red team - logger.debug("Player {} attempted to join white team", player.getId()); + logger.debug("Player {} attempted to join white team via teleporter", player.getId()); event.consume(); } else { - if (isBalancedTeams(targetTeam)) { - String team = setPlayerTeamToTeleporterTeam(player, entity); + if (teamSystem.isBalancedTeams(targetTeam)) { + LASTeamComponent teleporterTeamComponent = entity.getComponent(LASTeamComponent.class); + String team = teamSystem.setPlayerTeamToTeam(player, teleporterTeamComponent.team); handlePlayerTeleport(player, team); } else { EntityRef gameEntity = gameEntitySystem.getGameEntity(); @@ -91,34 +95,6 @@ public void onTeleportActivation(ActivateEvent event, EntityRef entity) { } } - private boolean isBalancedTeams(String targetTeam) { - gameEntitySystem.updateTeamStats(); - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - LASTeamStatsComponent teamStats = gameEntity.getComponent(LASTeamStatsComponent.class); - int maxTeamSizeDifference = gameEntity.getComponent(LASConfigComponent.class).maxTeamSizeDifference; - int currentTeamSizeDiff = teamStats.blackTeamSize - teamStats.redTeamSize; - int postTeleportTeamSizeDiff = targetTeam.equals(LASUtils.BLACK_TEAM) ? currentTeamSizeDiff + 1 : currentTeamSizeDiff - 1; - - return Math.abs(postTeleportTeamSizeDiff) <= maxTeamSizeDifference; - } - - private boolean isMinSizeTeams() { - gameEntitySystem.updateTeamStats(); - EntityRef gameEntity = gameEntitySystem.getGameEntity(); - LASTeamStatsComponent teamStats = gameEntity.getComponent(LASTeamStatsComponent.class); - int minTeamSize = gameEntity.getComponent(LASConfigComponent.class).minTeamSize; - - return teamStats.redTeamSize >= minTeamSize && teamStats.blackTeamSize >= minTeamSize; - } - - private String setPlayerTeamToTeleporterTeam(EntityRef player, EntityRef teleporter) { - LASTeamComponent teleporterTeamComponent = teleporter.getComponent(LASTeamComponent.class); - LASTeamComponent playerTeamComponent = player.getComponent(LASTeamComponent.class); - playerTeamComponent.team = teleporterTeamComponent.team; - player.saveComponent(playerTeamComponent); - return playerTeamComponent.team; - } - private void handlePlayerTeleport(EntityRef player, String team) { // when the first player joins the game, switch to pre-game phase if (phaseSystem.getCurrentPhase() == Phase.IDLE) { @@ -133,7 +109,7 @@ private void handlePlayerTeleport(EntityRef player, String team) { player.send(new RequestInventoryEvent(startingInventory.items)); // check game start condition - if (isMinSizeTeams() && phaseSystem.getCurrentPhase() == Phase.PRE_GAME) { + if (teamSystem.isMinSizeTeams() && phaseSystem.getCurrentPhase() == Phase.PRE_GAME) { sendEventToClients(TimerEvent::new); player.send(new DelayedDeactivateBarrierEvent(30000)); gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.COUNTDOWN)); From 013b3426cd71b0e846da1414642c2cf0b52a8c30 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 19:29:08 +0200 Subject: [PATCH 36/51] feat: add cleanup actions on arena exit --- .../systems/TeleporterSystem.java | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index ee8dfd39..93c6f7c4 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -20,6 +20,7 @@ import org.terasology.engine.logic.permission.PermissionManager; import org.terasology.engine.logic.players.SetDirectionEvent; import org.terasology.engine.network.ClientComponent; +import org.terasology.engine.network.events.DisconnectedEvent; import org.terasology.engine.registry.In; import org.terasology.engine.utilities.Assets; import org.terasology.gestalt.entitysystem.event.Event; @@ -30,7 +31,6 @@ import org.terasology.module.inventory.events.RequestInventoryEvent; import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.components.LASConfigComponent; -import org.terasology.module.lightandshadow.components.LASTeamStatsComponent; import org.terasology.module.lightandshadow.events.DelayedDeactivateBarrierEvent; import org.terasology.module.lightandshadow.events.TimerEvent; import org.terasology.module.lightandshadow.phases.Phase; @@ -129,11 +129,45 @@ public String teleportToPlatform(@Sender EntityRef sender) { ClientComponent clientComp = sender.getComponent(ClientComponent.class); LASTeamComponent senderTeam = clientComp.character.getComponent(LASTeamComponent.class); if (senderTeam != null && (senderTeam.team.equals(LASUtils.RED_TEAM) || senderTeam.team.equals(LASUtils.BLACK_TEAM))) { - // players teleporting back to platform should be removed from the playing teams - // white team (spectator) members are allowed to teleport without losing their team - clientComp.character.removeComponent(LASTeamComponent.class); + // spectators (white team) are not relevant for arena exit actions + performArenaExitActions(clientComp); } clientComp.character.send(new CharacterTeleportEvent(new Vector3f(LASUtils.FLOATING_PLATFORM_POSITION).add(0, 1, 0))); return "Teleporting you to the platform."; } + + @ReceiveEvent(components = ClientComponent.class) + public void onPlayerDisconnect(DisconnectedEvent event, EntityRef entity) { + ClientComponent clientComp = entity.getComponent(ClientComponent.class); + LASTeamComponent senderTeam = clientComp.character.getComponent(LASTeamComponent.class); + if (senderTeam != null && (senderTeam.team.equals(LASUtils.RED_TEAM) || senderTeam.team.equals(LASUtils.BLACK_TEAM))) { + // spectators (white team) are not relevant for arena exit actions + performArenaExitActions(clientComp); + } + } + + /** + * Follow-up actions necessary when a player leaves the arena, either by porting back to the platform + * or by disconnecting from the server. + * These action are required to verify the current game state and update the phase accordingly if necessary. + */ + private void performArenaExitActions(ClientComponent clientComp) { + // players teleporting back to platform should be removed from the playing teams + // white team (spectator) members are allowed to teleport without losing their team + clientComp.character.removeComponent(LASTeamComponent.class); + + // if in game phase: verify that game start condition still met + Phase currentPhase = phaseSystem.getCurrentPhase(); + if ((currentPhase == Phase.IN_GAME || currentPhase == Phase.COUNTDOWN) && !teamSystem.isMinSizeTeams()) { + logger.debug("Starting condition no longer met, switching from phase {} to PRE_GAME", currentPhase); + gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.PRE_GAME)); + } + + // if the player teleporting was the last one still in the arena + // switch back to idle phase + if (teamSystem.getTeamSize(LASUtils.BLACK_TEAM) == 0 && teamSystem.getTeamSize(LASUtils.RED_TEAM) == 0) { + logger.debug("No players left in arena, switching to IDLE phase"); + gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.IDLE)); + } + } } From 6ffee54d5005abad8f57ed1b45c5820372b2a280 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 19:41:06 +0200 Subject: [PATCH 37/51] refactor: let TeamSystem handle adding player to team --- .../module/lightandshadow/systems/TeamSystem.java | 14 +++++++++----- .../lightandshadow/systems/TeleporterSystem.java | 7 +++---- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java index 5d491abd..b88b16f1 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java @@ -52,11 +52,13 @@ public boolean isMinSizeTeams() { return teamStats.redTeamSize >= minTeamSize && teamStats.blackTeamSize >= minTeamSize; } - public String setPlayerTeamToTeam(EntityRef player, String team) { - LASTeamComponent playerTeamComponent = player.getComponent(LASTeamComponent.class); - playerTeamComponent.team = team; - player.saveComponent(playerTeamComponent); - return playerTeamComponent.team; + public boolean setPlayerTeamToTeam(EntityRef player, String team) { + if (!isBalancedTeams(team)) { + return false; + } + LASTeamComponent playerTeamComponent = new LASTeamComponent(team); + player.addOrSaveComponent(playerTeamComponent); + return true; } public int getTeamSize(String team) { @@ -71,6 +73,8 @@ public int getTeamSize(String team) { case LASUtils.WHITE_TEAM: return teamStats.whiteTeamSize; } + logger.error("Was requested to get team size for non-existant team {}", team); + return 0; } } diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index 93c6f7c4..8f7bade5 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -81,10 +81,9 @@ public void onTeleportActivation(ActivateEvent event, EntityRef entity) { logger.debug("Player {} attempted to join white team via teleporter", player.getId()); event.consume(); } else { - if (teamSystem.isBalancedTeams(targetTeam)) { - LASTeamComponent teleporterTeamComponent = entity.getComponent(LASTeamComponent.class); - String team = teamSystem.setPlayerTeamToTeam(player, teleporterTeamComponent.team); - handlePlayerTeleport(player, team); + if (teamSystem.setPlayerTeamToTeam(player, targetTeam)) { + logger.debug("Player {} successfully added to team {}", player, targetTeam); + handlePlayerTeleport(player, targetTeam); } else { EntityRef gameEntity = gameEntitySystem.getGameEntity(); LASConfigComponent config = gameEntity.getComponent(LASConfigComponent.class); From ae24b83bcab0044389298f3f88a3115d89e196de Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 19:47:09 +0200 Subject: [PATCH 38/51] feat: always spawn players on platform --- .../terasology/module/lightandshadow/systems/LASSystem.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/LASSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/LASSystem.java index 773d2c9a..6e13f2b7 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/LASSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/LASSystem.java @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 package org.terasology.module.lightandshadow.systems; +import org.joml.Vector3f; import org.terasology.economy.components.CurrencyStorageComponent; import org.terasology.economy.events.WalletUpdatedEvent; import org.terasology.engine.entitySystem.entity.EntityRef; @@ -9,6 +10,7 @@ import org.terasology.engine.entitySystem.event.Priority; import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterSystem; +import org.terasology.engine.logic.characters.CharacterTeleportEvent; import org.terasology.engine.logic.players.event.OnPlayerSpawnedEvent; import org.terasology.engine.registry.In; import org.terasology.engine.world.sun.CelestialSystem; @@ -16,6 +18,7 @@ import org.terasology.module.inventory.components.InventoryComponent; import org.terasology.module.inventory.events.RemoveItemAction; import org.terasology.module.inventory.systems.InventoryManager; +import org.terasology.module.lightandshadow.LASUtils; @RegisterSystem public class LASSystem extends BaseComponentSystem { @@ -39,6 +42,7 @@ public void onPlayerSpawn(OnPlayerSpawnedEvent event, EntityRef player, Inventor EntityRef gameEntity = gameEntitySystem.getGameEntity(); player.getComponent(CurrencyStorageComponent.class).amount = gameEntity.getComponent(CurrencyStorageComponent.class).amount; player.send(new WalletUpdatedEvent(gameEntity.getComponent(CurrencyStorageComponent.class).amount)); + player.send(new CharacterTeleportEvent(new Vector3f(LASUtils.FLOATING_PLATFORM_POSITION).add(0, 1, 0))); } @Override From bffd298a2e28c1b8bd01cac1f6f35aace442e1a3 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 19:52:46 +0200 Subject: [PATCH 39/51] refactor: rename ClientGameStateInfoSystem to ClientInformationSystem --- ...entGameStateInfoSystem.java => ClientInformationSystem.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/org/terasology/module/lightandshadow/systems/{ClientGameStateInfoSystem.java => ClientInformationSystem.java} (98%) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientGameStateInfoSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java similarity index 98% rename from src/main/java/org/terasology/module/lightandshadow/systems/ClientGameStateInfoSystem.java rename to src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java index b2aa0417..4f51dbcd 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientGameStateInfoSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java @@ -29,7 +29,7 @@ import java.util.TimerTask; @RegisterSystem(RegisterMode.CLIENT) -public class ClientGameStateInfoSystem extends BaseComponentSystem { +public class ClientInformationSystem extends BaseComponentSystem { private static final String WAIT_NOTIFICATION_ID = "LightAndShadow:waitForPlayers"; public static final ResourceUrn ASSET_URI = new ResourceUrn("LightAndShadow:Timer"); From 0e12a66a28e4ae334c2828855b18860bb3603d6b Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 19:55:38 +0200 Subject: [PATCH 40/51] refactor: merge ClientStatisticsSystem into ClientInformationSystem --- .../systems/ClientInformationSystem.java | 64 ++++++++++++ .../systems/ClientStatisticsSystem.java | 97 ------------------- 2 files changed, 64 insertions(+), 97 deletions(-) delete mode 100644 src/main/java/org/terasology/module/lightandshadow/systems/ClientStatisticsSystem.java diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java index 4f51dbcd..51e08a90 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java @@ -2,19 +2,27 @@ // SPDX-License-Identifier: Apache-2.0 package org.terasology.module.lightandshadow.systems; +import org.terasology.engine.core.SimpleUri; import org.terasology.engine.entitySystem.entity.EntityManager; import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.engine.entitySystem.event.EventPriority; +import org.terasology.engine.entitySystem.event.Priority; import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterMode; import org.terasology.engine.entitySystem.systems.RegisterSystem; import org.terasology.engine.input.InputSystem; import org.terasology.engine.logic.players.LocalPlayer; +import org.terasology.engine.logic.players.event.LocalPlayerInitializedEvent; import org.terasology.engine.logic.players.event.OnPlayerSpawnedEvent; import org.terasology.engine.registry.In; import org.terasology.engine.rendering.nui.NUIManager; +import org.terasology.engine.rendering.nui.layers.ingame.DeathScreen; import org.terasology.gestalt.assets.ResourceUrn; import org.terasology.gestalt.entitysystem.event.ReceiveEvent; +import org.terasology.module.lightandshadow.LASUtils; +import org.terasology.module.lightandshadow.TapButton; import org.terasology.module.lightandshadow.components.LASConfigComponent; +import org.terasology.module.lightandshadow.events.ScoreUpdateFromServerEvent; import org.terasology.module.lightandshadow.events.TimerEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseEndedEvent; @@ -31,6 +39,7 @@ @RegisterSystem(RegisterMode.CLIENT) public class ClientInformationSystem extends BaseComponentSystem { private static final String WAIT_NOTIFICATION_ID = "LightAndShadow:waitForPlayers"; + private static final String STATS_NOTIFICATION_ID = "LightAndShadow:firstTimeStatistics"; public static final ResourceUrn ASSET_URI = new ResourceUrn("LightAndShadow:Timer"); @@ -52,12 +61,25 @@ public class ClientInformationSystem extends BaseComponentSystem { @In private LocalPlayer localPlayer; + @In + private ClientGameOverSystem clientGameOverSystem; + + private DeathScreen statisticsScreen; + private int redScore = 0; + private int blackScore = 0; + private boolean statsScreenIsOpen; + private boolean isExpired; private DialogNotificationOverlay window; @Override public void initialise() { window = nuiManager.addOverlay(ASSET_URI, DialogNotificationOverlay.class); + statisticsScreen = nuiManager.createScreen("LightAndShadow:statisticsScreen", DeathScreen.class); + clientGameOverSystem.addGoalScore(statisticsScreen, "spadesGoalScore"); + clientGameOverSystem.addGoalScore(statisticsScreen, "heartsGoalScore"); + clientGameOverSystem.addTeamScore(statisticsScreen, "heartsTeamScore", redScore); + clientGameOverSystem.addTeamScore(statisticsScreen, "spadesTeamScore", blackScore); } @Override @@ -82,6 +104,48 @@ public void onPregameEnd(OnPreGamePhaseEndedEvent event, EntityRef entity) { localPlayer.getClientEntity().send(new ExpireNotificationEvent(WAIT_NOTIFICATION_ID)); } + @ReceiveEvent + public void onScoreUpdate(ScoreUpdateFromServerEvent event, EntityRef entity) { + String team = event.team; + if (team.equals(LASUtils.RED_TEAM)) { + redScore = event.score; + } + if (team.equals(LASUtils.BLACK_TEAM)) { + blackScore = event.score; + } + } + + @Priority(EventPriority.PRIORITY_CRITICAL) + @ReceiveEvent + public void onTab(TapButton event, EntityRef entity) { + if (event.isDown()) { + if (localPlayer.getClientEntity().equals(entity) && !statsScreenIsOpen) { + if (!isExpired) { + entity.send(new ExpireNotificationEvent(STATS_NOTIFICATION_ID)); + isExpired = true; + } + clientGameOverSystem.addTeamScore(statisticsScreen, "spadesTeamScore", blackScore); + clientGameOverSystem.addTeamScore(statisticsScreen, "heartsTeamScore", redScore); + clientGameOverSystem.addPlayerStatisticsInfo(statisticsScreen); + nuiManager.pushScreen("LightAndShadow:statisticsScreen"); + statsScreenIsOpen = true; + event.consume(); + } + } else { + statsScreenIsOpen = false; + nuiManager.closeScreen("LightAndShadow:statisticsScreen"); + } + } + + @ReceiveEvent + public void onLocalPlayerInitialized(LocalPlayerInitializedEvent event, EntityRef entity) { + Notification notification = new Notification(STATS_NOTIFICATION_ID, + "The Numbers Game", + "Hold " + LASUtils.getActivationKey(inputSystem, new SimpleUri("LightAndShadow:statistics")) + " to see statistics", + "engine:items#blueBook"); + localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); + } + @ReceiveEvent public void updateTimer(TimerEvent event, EntityRef entity) { if (localPlayer.getClientEntity().equals(entity)) { diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientStatisticsSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientStatisticsSystem.java deleted file mode 100644 index c103f612..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientStatisticsSystem.java +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.systems; - -import org.terasology.engine.core.SimpleUri; -import org.terasology.engine.entitySystem.entity.EntityRef; -import org.terasology.engine.entitySystem.event.EventPriority; -import org.terasology.engine.entitySystem.event.Priority; -import org.terasology.engine.entitySystem.systems.BaseComponentSystem; -import org.terasology.engine.entitySystem.systems.RegisterMode; -import org.terasology.engine.entitySystem.systems.RegisterSystem; -import org.terasology.engine.input.InputSystem; -import org.terasology.engine.logic.players.LocalPlayer; -import org.terasology.engine.logic.players.event.LocalPlayerInitializedEvent; -import org.terasology.engine.registry.In; -import org.terasology.engine.rendering.nui.NUIManager; -import org.terasology.engine.rendering.nui.layers.ingame.DeathScreen; -import org.terasology.gestalt.entitysystem.event.ReceiveEvent; -import org.terasology.module.lightandshadow.LASUtils; -import org.terasology.module.lightandshadow.events.ScoreUpdateFromServerEvent; -import org.terasology.module.lightandshadow.TapButton; -import org.terasology.notifications.events.ExpireNotificationEvent; -import org.terasology.notifications.events.ShowNotificationEvent; -import org.terasology.notifications.model.Notification; - -/** - * Displays statistics screen when required. - */ -@RegisterSystem(RegisterMode.CLIENT) -public class ClientStatisticsSystem extends BaseComponentSystem { - private static final String NOTIFICATION_ID = "LightAndShadow:firstTimeStatistics"; - - @In - InputSystem inputSystem; - @In - private NUIManager nuiManager; - @In - private LocalPlayer localPlayer; - @In - private ClientGameOverSystem clientGameOverSystem; - private DeathScreen statisticsScreen; - private int redScore = 0; - private int blackScore = 0; - private boolean isOpen; - private boolean isExpired; - - @Override - public void initialise() { - statisticsScreen = nuiManager.createScreen("LightAndShadow:statisticsScreen", DeathScreen.class); - clientGameOverSystem.addGoalScore(statisticsScreen, "spadesGoalScore"); - clientGameOverSystem.addGoalScore(statisticsScreen, "heartsGoalScore"); - clientGameOverSystem.addTeamScore(statisticsScreen, "heartsTeamScore", redScore); - clientGameOverSystem.addTeamScore(statisticsScreen, "spadesTeamScore", blackScore); - - } - @ReceiveEvent - public void onScoreUpdate(ScoreUpdateFromServerEvent event, EntityRef entity) { - String team = event.team; - if (team.equals(LASUtils.RED_TEAM)) { - redScore = event.score; - } - if (team.equals(LASUtils.BLACK_TEAM)) { - blackScore = event.score; - } - } - - @Priority(EventPriority.PRIORITY_CRITICAL) - @ReceiveEvent - public void onTab(TapButton event, EntityRef entity) { - if (event.isDown()) { - if (localPlayer.getClientEntity().equals(entity) && !isOpen) { - if (!isExpired) { - entity.send(new ExpireNotificationEvent(NOTIFICATION_ID)); - isExpired = true; - } - clientGameOverSystem.addTeamScore(statisticsScreen, "spadesTeamScore", blackScore); - clientGameOverSystem.addTeamScore(statisticsScreen, "heartsTeamScore", redScore); - clientGameOverSystem.addPlayerStatisticsInfo(statisticsScreen); - nuiManager.pushScreen("LightAndShadow:statisticsScreen"); - isOpen = true; - event.consume(); - } - } else { - isOpen = false; - nuiManager.closeScreen("LightAndShadow:statisticsScreen"); - } - } - - @ReceiveEvent - public void onLocalPlayerInitialized(LocalPlayerInitializedEvent event, EntityRef entity) { - Notification notification = new Notification(NOTIFICATION_ID, - "The Numbers Game", - "Hold " + LASUtils.getActivationKey(inputSystem, new SimpleUri("LightAndShadow:statistics")) + " to see statistics", - "engine:items#blueBook"); - localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); - } -} From 806f18de95be9252831ad0a61b3ed71751bfabb3 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 20:00:26 +0200 Subject: [PATCH 41/51] feat: cancel timer if no longer countdown phase, switch to in-game phase when countdown finished --- .../lightandshadow/systems/ClientInformationSystem.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java index 51e08a90..ace68cb6 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java @@ -27,6 +27,7 @@ import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseEndedEvent; import org.terasology.module.lightandshadow.phases.Phase; +import org.terasology.module.lightandshadow.phases.SwitchToPhaseEvent; import org.terasology.module.lightandshadow.phases.authority.PhaseSystem; import org.terasology.notifications.events.ExpireNotificationEvent; import org.terasology.notifications.events.ShowNotificationEvent; @@ -154,6 +155,12 @@ public void updateTimer(TimerEvent event, EntityRef entity) { int timePeriod = COUNTDOWN_IN_SECONDS; boolean addNotification; public void run() { + if (phaseSystem.getCurrentPhase() != Phase.COUNTDOWN) { + if (!addNotification) { + window.removeNotification("The game starts in " + (timePeriod + 1) + " seconds."); + } + timer.cancel(); + } if (addNotification) { entity.send(new ExpireNotificationEvent(WAIT_NOTIFICATION_ID)); window.addNotification("The game starts in " + timePeriod-- + " seconds."); @@ -165,6 +172,7 @@ public void run() { } if (timePeriod < 0) { window.removeNotification("The game starts in " + (timePeriod + 1) + " seconds."); + gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.IN_GAME)); timer.cancel(); } } From a1eae079b45d27f48eafd87e9ead4050c3481f03 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 20:18:10 +0200 Subject: [PATCH 42/51] feat: remove barriers on phase switch PreGame -> InGame --- .../events/DelayedDeactivateBarrierEvent.java | 22 --------- .../lightandshadow/systems/AttackSystem.java | 6 +++ .../systems/MagicDomeSystem.java | 45 +++++++------------ .../systems/TeleporterSystem.java | 2 - 4 files changed, 21 insertions(+), 54 deletions(-) delete mode 100644 src/main/java/org/terasology/module/lightandshadow/events/DelayedDeactivateBarrierEvent.java diff --git a/src/main/java/org/terasology/module/lightandshadow/events/DelayedDeactivateBarrierEvent.java b/src/main/java/org/terasology/module/lightandshadow/events/DelayedDeactivateBarrierEvent.java deleted file mode 100644 index 3f504234..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/events/DelayedDeactivateBarrierEvent.java +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.events; - -import org.terasology.engine.network.ServerEvent; -import org.terasology.gestalt.entitysystem.event.Event; - -/** - * Trigger event to deactivate the pregame barriers. - */ -@ServerEvent -public class DelayedDeactivateBarrierEvent implements Event { - private int delay; - - public DelayedDeactivateBarrierEvent(int delay) { - this.delay = delay; - } - - public int getDelay() { - return delay; - } -} diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java index d2ba2545..d32dba82 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/AttackSystem.java @@ -28,6 +28,7 @@ import org.terasology.module.lightandshadow.events.OnFlagDropEvent; import org.terasology.module.lightandshadow.events.OnFlagPickupEvent; import org.terasology.module.lightandshadow.events.ReturnFlagEvent; +import org.terasology.module.lightandshadow.phases.OnInGamePhaseStartedEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; @RegisterSystem(RegisterMode.AUTHORITY) @@ -81,6 +82,11 @@ public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { entity.addComponent(new InvulnerableComponent()); } + @ReceiveEvent + public void onGameStart(OnInGamePhaseStartedEvent event, EntityRef entity) { + entity.removeComponent(InvulnerableComponent.class); + } + @Priority(EventPriority.PRIORITY_HIGH) @ReceiveEvent(components = InvulnerableComponent.class) public void preventFriendlyFire(BeforeDamagedEvent event, EntityRef entity) { diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java index f0e1d922..561a7f88 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/MagicDomeSystem.java @@ -13,25 +13,21 @@ import org.terasology.engine.logic.characters.CharacterImpulseEvent; import org.terasology.engine.logic.characters.CharacterMoveInputEvent; import org.terasology.engine.logic.delay.DelayManager; -import org.terasology.engine.logic.delay.DelayedActionTriggeredEvent; import org.terasology.engine.logic.location.LocationComponent; import org.terasology.engine.registry.In; import org.terasology.gestalt.entitysystem.event.ReceiveEvent; import org.terasology.lightandshadowresources.components.LASTeamComponent; import org.terasology.module.lightandshadow.LASUtils; -import org.terasology.module.lightandshadow.components.InvulnerableComponent; import org.terasology.module.lightandshadow.components.MagicDome; -import org.terasology.module.lightandshadow.events.DelayedDeactivateBarrierEvent; +import org.terasology.module.lightandshadow.phases.OnInGamePhaseStartedEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; import org.terasology.module.lightandshadow.phases.Phase; import org.terasology.module.lightandshadow.phases.authority.PhaseSystem; @RegisterSystem(RegisterMode.AUTHORITY) public class MagicDomeSystem extends BaseComponentSystem { - private static final String DEACTIVATE_BARRIERS_ACTION = "LightAndShadow:deactivateBarriers"; private static final int PREGAME_ZONE_RADIUS = 20; - @In - DelayManager delayManager; +; @In PhaseSystem phaseSystem; @In @@ -42,6 +38,16 @@ public class MagicDomeSystem extends BaseComponentSystem { private EntityRef blackBarrier = EntityRef.NULL; + @ReceiveEvent + public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { + activateBarriers(); + } + + @ReceiveEvent + public void onGameStart(OnInGamePhaseStartedEvent event, EntityRef entity) { + deactivateBarriers(); + } + /** * Activates the barriers for the pregame regions corresponding to both the teams only in the beginning when * the barriers haven't been created yet. @@ -53,23 +59,9 @@ private void activateBarriers() { } } - @ReceiveEvent - public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { - activateBarriers(); - } - - @ReceiveEvent - public void delayedDeactivateBarriers(DelayedDeactivateBarrierEvent event, EntityRef entity) { - delayManager.addDelayedAction(entity, DEACTIVATE_BARRIERS_ACTION, event.getDelay()); - } - - @ReceiveEvent - public void deactivateBarriers(DelayedActionTriggeredEvent event, EntityRef entity) { - if (event.getActionId().equals(DEACTIVATE_BARRIERS_ACTION)) { - redBarrier.destroy(); - blackBarrier.destroy(); - removePlayerInvulnerableComponents(); - } + public void deactivateBarriers() { + redBarrier.destroy(); + blackBarrier.destroy(); } @@ -126,11 +118,4 @@ public boolean isAllowedInsideBarrier(EntityRef player, EntityRef barrier) { String playerTeam = player.getComponent(LASTeamComponent.class).team; return barrierTeam.equals(playerTeam); } - - private void removePlayerInvulnerableComponents() { - Iterable players = entityManager.getEntitiesWith(InvulnerableComponent.class); - for (EntityRef player : players) { - player.removeComponent(InvulnerableComponent.class); - } - } } diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index 8f7bade5..0b8b5dae 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -31,7 +31,6 @@ import org.terasology.module.inventory.events.RequestInventoryEvent; import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.components.LASConfigComponent; -import org.terasology.module.lightandshadow.events.DelayedDeactivateBarrierEvent; import org.terasology.module.lightandshadow.events.TimerEvent; import org.terasology.module.lightandshadow.phases.Phase; import org.terasology.module.lightandshadow.phases.SwitchToPhaseEvent; @@ -110,7 +109,6 @@ private void handlePlayerTeleport(EntityRef player, String team) { // check game start condition if (teamSystem.isMinSizeTeams() && phaseSystem.getCurrentPhase() == Phase.PRE_GAME) { sendEventToClients(TimerEvent::new); - player.send(new DelayedDeactivateBarrierEvent(30000)); gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.COUNTDOWN)); } } From 1af81712d5342ce83f5bebb6edca8bb2c502458d Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 21:47:56 +0200 Subject: [PATCH 43/51] feat: switch to postGame phase once one team won --- .../module/lightandshadow/systems/ScoreSystem.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ScoreSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ScoreSystem.java index 9782e3ee..6468048e 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ScoreSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ScoreSystem.java @@ -23,6 +23,8 @@ import org.terasology.module.lightandshadow.events.RestartRequestEvent; import org.terasology.module.lightandshadow.events.ReturnFlagEvent; import org.terasology.module.lightandshadow.events.ScoreUpdateFromServerEvent; +import org.terasology.module.lightandshadow.phases.Phase; +import org.terasology.module.lightandshadow.phases.SwitchToPhaseEvent; /** * System responsible for calculating and providing score. @@ -34,6 +36,8 @@ public class ScoreSystem extends BaseComponentSystem { private InventoryManager inventoryManager; @In private EntityManager entityManager; + @In + GameEntitySystem gameEntitySystem; private int redScore; private int blackScore; @@ -87,10 +91,12 @@ private void checkAndResetGameOnScore(ActivateEvent event, EntityRef entity) { if (redScore >= LASUtils.GOAL_SCORE) { resetLevel(); sendEventToClients(new GameOverEvent(LASUtils.RED_TEAM, blackScore, redScore)); + gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.POST_GAME)); } if (blackScore >= LASUtils.GOAL_SCORE) { resetLevel(); sendEventToClients(new GameOverEvent(LASUtils.BLACK_TEAM, blackScore, redScore)); + gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.POST_GAME)); } } } From 072b5c193c10beee80a1b32475120a2ac3bfd137 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 21:51:23 +0200 Subject: [PATCH 44/51] feat: switch to preGame phase on restart request --- .../module/lightandshadow/systems/RestartSystem.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/RestartSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/RestartSystem.java index cb3846e1..98da2557 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/RestartSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/RestartSystem.java @@ -20,6 +20,8 @@ import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.events.ClientRestartEvent; import org.terasology.module.lightandshadow.events.RestartRequestEvent; +import org.terasology.module.lightandshadow.phases.Phase; +import org.terasology.module.lightandshadow.phases.SwitchToPhaseEvent; /** * System to invoke restart of a game round. @@ -52,5 +54,6 @@ public void onRestartRequest(RestartRequestEvent event, EntityRef clientEntity, sub(LASUtils.getTeleportDestination(LASUtils.getOppositionTeam(team)), new Vector3f())), 0)); client.send(new ClientRestartEvent()); } + gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.PRE_GAME)); } } From 4ed4709fbb7fe5844df1491e4ca2ec6f1cddc3e1 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 21:58:41 +0200 Subject: [PATCH 45/51] refactor: merge ClientGameOverSystem into ClientInformationSystem --- .../systems/ClientGameOverSystem.java | 156 ------------------ .../systems/ClientInformationSystem.java | 131 ++++++++++++++- 2 files changed, 122 insertions(+), 165 deletions(-) delete mode 100644 src/main/java/org/terasology/module/lightandshadow/systems/ClientGameOverSystem.java diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientGameOverSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientGameOverSystem.java deleted file mode 100644 index 8a67486e..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientGameOverSystem.java +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.systems; - -import org.terasology.engine.entitySystem.entity.EntityManager; -import org.terasology.engine.entitySystem.entity.EntityRef; -import org.terasology.engine.entitySystem.systems.BaseComponentSystem; -import org.terasology.engine.entitySystem.systems.RegisterMode; -import org.terasology.engine.entitySystem.systems.RegisterSystem; -import org.terasology.engine.logic.players.LocalPlayer; -import org.terasology.engine.logic.players.PlayerCharacterComponent; -import org.terasology.engine.logic.players.PlayerUtil; -import org.terasology.engine.network.ClientComponent; -import org.terasology.engine.registry.In; -import org.terasology.engine.registry.Share; -import org.terasology.engine.rendering.nui.NUIManager; -import org.terasology.engine.rendering.nui.layers.ingame.DeathScreen; -import org.terasology.gestalt.entitysystem.event.ReceiveEvent; -import org.terasology.lightandshadowresources.components.LASTeamComponent; -import org.terasology.module.lightandshadow.LASUtils; -import org.terasology.module.lightandshadow.components.PlayerStatisticsComponent; -import org.terasology.module.lightandshadow.events.GameOverEvent; -import org.terasology.module.lightandshadow.events.RestartRequestEvent; -import org.terasology.nui.WidgetUtil; -import org.terasology.nui.layouts.miglayout.MigLayout; -import org.terasology.nui.widgets.UIButton; -import org.terasology.nui.widgets.UILabel; - -import java.util.Timer; -import java.util.TimerTask; - -/** - * Displays game over screen for all clients. - */ -@RegisterSystem(RegisterMode.CLIENT) -@Share(value = ClientGameOverSystem.class) -public class ClientGameOverSystem extends BaseComponentSystem { - - private static Timer timer; - - private DeathScreen deathScreen; - - @In - private NUIManager nuiManager; - @In - private LocalPlayer localPlayer; - @In - private EntityManager entityManager; - - @Override - public void initialise() { - deathScreen = nuiManager.createScreen(LASUtils.DEATH_SCREEN, DeathScreen.class); - } - - /** - * System to show game over screen once a team achieves goal score. - * - * @param event the GameOverEvent event which stores the winning team and the final scores of both teams. - * @param entity the entity about each player connected to the game. TODO: needs more details/clarification - */ - @ReceiveEvent - public void onGameOver(GameOverEvent event, EntityRef entity) { - if (localPlayer.getClientEntity().equals(entity)) { - nuiManager.removeOverlay(LASUtils.ONLINE_PLAYERS_OVERLAY); - nuiManager.pushScreen(deathScreen); - addPlayerStatisticsInfo(deathScreen); - addFlagInfo(deathScreen, event); - UILabel gameOverResult = deathScreen.find("gameOverResult", UILabel.class); - - UIButton restartButton = deathScreen.find("restart", UIButton.class); - UILabel countDown = deathScreen.find("timer", UILabel.class); - if (restartButton != null && countDown != null) { - timer = new Timer(); - timer.scheduleAtFixedRate(new TimerTask() { - int timePeriod = 10; - public void run() { - countDown.setText(Integer.toString(timePeriod--)); - if (timePeriod < 0) { - countDown.setText(" "); - restartButton.setEnabled(true); - timer.cancel(); - } - } - }, 0, 1000); - restartButton.setVisible(true); - restartButton.setEnabled(false); - } - - WidgetUtil.trySubscribe(deathScreen, "restart", widget -> triggerRestart()); - if (gameOverResult != null) { - if (event.winningTeam.equals(localPlayer.getCharacterEntity().getComponent(LASTeamComponent.class).team)) { - gameOverResult.setText("Victory"); - gameOverResult.setFamily("win"); - } else { - gameOverResult.setText("Defeat"); - gameOverResult.setFamily("lose"); - } - } - } - } - - public void addPlayerStatisticsInfo(DeathScreen deathScreen) { - MigLayout spadesTeamMigLayout = deathScreen.find("spadesTeamPlayerStatistics", MigLayout.class); - MigLayout heartsTeamMigLayout = deathScreen.find("heartsTeamPlayerStatistics", MigLayout.class); - spadesTeamMigLayout.removeAllWidgets(); - heartsTeamMigLayout.removeAllWidgets(); - if (spadesTeamMigLayout != null && heartsTeamMigLayout != null) { - Iterable characters = entityManager.getEntitiesWith(PlayerCharacterComponent.class, - LASTeamComponent.class); - - for (EntityRef character : characters) { - EntityRef client = character.getOwner(); - ClientComponent clientComponent = client.getComponent(ClientComponent.class); - String playerTeam = character.getComponent(LASTeamComponent.class).team; - PlayerStatisticsComponent playerStatisticsComponent = - character.getComponent(PlayerStatisticsComponent.class); - MigLayout migLayout = (playerTeam.equals("black") ? spadesTeamMigLayout : heartsTeamMigLayout); - if (!playerTeam.equals("white")) { - addInfoToTeamMigLayout(migLayout, clientComponent, playerStatisticsComponent); - } - } - } - } - - private void addInfoToTeamMigLayout(MigLayout migLayout, ClientComponent clientComponent, - PlayerStatisticsComponent playerStatisticsComponent) { - migLayout.addWidget(new UILabel(PlayerUtil.getColoredPlayerName(clientComponent.clientInfo)), - new MigLayout.CCHint()); - migLayout.addWidget(new UILabel(String.valueOf(playerStatisticsComponent.kills)), new MigLayout.CCHint()); - migLayout.addWidget(new UILabel(String.valueOf(playerStatisticsComponent.deaths)), new MigLayout.CCHint("wrap" - )); - playerStatisticsComponent.kills = 0; - playerStatisticsComponent.deaths = 0; - } - - private void addFlagInfo(DeathScreen deathScreen, GameOverEvent event) { - addTeamScore(deathScreen, "spadesTeamScore", event.blackTeamScore); - addTeamScore(deathScreen, "heartsTeamScore", event.redTeamScore); - addGoalScore(deathScreen, "spadesGoalScore"); - addGoalScore(deathScreen, "heartsGoalScore"); - } - - public void addTeamScore(DeathScreen deathScreen, String teamUILabelId, int finalScore) { - UILabel teamScore = deathScreen.find(teamUILabelId, UILabel.class); - teamScore.setText(String.valueOf(finalScore)); - } - - public void addGoalScore(DeathScreen deathScreen, String teamUILabelID) { - UILabel goalScore = deathScreen.find(teamUILabelID, UILabel.class); - goalScore.setText(Integer.toString(LASUtils.GOAL_SCORE)); - } - - private void triggerRestart() { - localPlayer.getClientEntity().send(new RestartRequestEvent()); - } -} diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java index ace68cb6..832457d0 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java @@ -12,16 +12,23 @@ import org.terasology.engine.entitySystem.systems.RegisterSystem; import org.terasology.engine.input.InputSystem; import org.terasology.engine.logic.players.LocalPlayer; +import org.terasology.engine.logic.players.PlayerCharacterComponent; +import org.terasology.engine.logic.players.PlayerUtil; import org.terasology.engine.logic.players.event.LocalPlayerInitializedEvent; import org.terasology.engine.logic.players.event.OnPlayerSpawnedEvent; +import org.terasology.engine.network.ClientComponent; import org.terasology.engine.registry.In; import org.terasology.engine.rendering.nui.NUIManager; import org.terasology.engine.rendering.nui.layers.ingame.DeathScreen; import org.terasology.gestalt.assets.ResourceUrn; import org.terasology.gestalt.entitysystem.event.ReceiveEvent; +import org.terasology.lightandshadowresources.components.LASTeamComponent; import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.TapButton; import org.terasology.module.lightandshadow.components.LASConfigComponent; +import org.terasology.module.lightandshadow.components.PlayerStatisticsComponent; +import org.terasology.module.lightandshadow.events.GameOverEvent; +import org.terasology.module.lightandshadow.events.RestartRequestEvent; import org.terasology.module.lightandshadow.events.ScoreUpdateFromServerEvent; import org.terasology.module.lightandshadow.events.TimerEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; @@ -33,6 +40,10 @@ import org.terasology.notifications.events.ShowNotificationEvent; import org.terasology.notifications.model.Notification; import org.terasology.notify.ui.DialogNotificationOverlay; +import org.terasology.nui.WidgetUtil; +import org.terasology.nui.layouts.miglayout.MigLayout; +import org.terasology.nui.widgets.UIButton; +import org.terasology.nui.widgets.UILabel; import java.util.Timer; import java.util.TimerTask; @@ -62,10 +73,9 @@ public class ClientInformationSystem extends BaseComponentSystem { @In private LocalPlayer localPlayer; - @In - private ClientGameOverSystem clientGameOverSystem; private DeathScreen statisticsScreen; + private DeathScreen deathScreen; private int redScore = 0; private int blackScore = 0; private boolean statsScreenIsOpen; @@ -76,11 +86,12 @@ public class ClientInformationSystem extends BaseComponentSystem { @Override public void initialise() { window = nuiManager.addOverlay(ASSET_URI, DialogNotificationOverlay.class); + deathScreen = nuiManager.createScreen(LASUtils.DEATH_SCREEN, DeathScreen.class); statisticsScreen = nuiManager.createScreen("LightAndShadow:statisticsScreen", DeathScreen.class); - clientGameOverSystem.addGoalScore(statisticsScreen, "spadesGoalScore"); - clientGameOverSystem.addGoalScore(statisticsScreen, "heartsGoalScore"); - clientGameOverSystem.addTeamScore(statisticsScreen, "heartsTeamScore", redScore); - clientGameOverSystem.addTeamScore(statisticsScreen, "spadesTeamScore", blackScore); + addGoalScore(statisticsScreen, "spadesGoalScore"); + addGoalScore(statisticsScreen, "heartsGoalScore"); + addTeamScore(statisticsScreen, "heartsTeamScore", redScore); + addTeamScore(statisticsScreen, "spadesTeamScore", blackScore); } @Override @@ -125,9 +136,9 @@ public void onTab(TapButton event, EntityRef entity) { entity.send(new ExpireNotificationEvent(STATS_NOTIFICATION_ID)); isExpired = true; } - clientGameOverSystem.addTeamScore(statisticsScreen, "spadesTeamScore", blackScore); - clientGameOverSystem.addTeamScore(statisticsScreen, "heartsTeamScore", redScore); - clientGameOverSystem.addPlayerStatisticsInfo(statisticsScreen); + addTeamScore(statisticsScreen, "spadesTeamScore", blackScore); + addTeamScore(statisticsScreen, "heartsTeamScore", redScore); + addPlayerStatisticsInfo(statisticsScreen); nuiManager.pushScreen("LightAndShadow:statisticsScreen"); statsScreenIsOpen = true; event.consume(); @@ -147,6 +158,53 @@ public void onLocalPlayerInitialized(LocalPlayerInitializedEvent event, EntityRe localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); } + /** + * System to show game over screen once a team achieves goal score. + * + * @param event the GameOverEvent event which stores the winning team and the final scores of both teams. + * @param entity the entity about each player connected to the game. TODO: needs more details/clarification + */ + @ReceiveEvent + public void onGameOver(GameOverEvent event, EntityRef entity) { + if (localPlayer.getClientEntity().equals(entity)) { + nuiManager.removeOverlay(LASUtils.ONLINE_PLAYERS_OVERLAY); + nuiManager.pushScreen(deathScreen); + addPlayerStatisticsInfo(deathScreen); + addFlagInfo(deathScreen, event); + UILabel gameOverResult = deathScreen.find("gameOverResult", UILabel.class); + + UIButton restartButton = deathScreen.find("restart", UIButton.class); + UILabel countDown = deathScreen.find("timer", UILabel.class); + if (restartButton != null && countDown != null) { + timer = new Timer(); + timer.scheduleAtFixedRate(new TimerTask() { + int timePeriod = 10; + public void run() { + countDown.setText(Integer.toString(timePeriod--)); + if (timePeriod < 0) { + countDown.setText(" "); + restartButton.setEnabled(true); + timer.cancel(); + } + } + }, 0, 1000); + restartButton.setVisible(true); + restartButton.setEnabled(false); + } + + WidgetUtil.trySubscribe(deathScreen, "restart", widget -> triggerRestart()); + if (gameOverResult != null) { + if (event.winningTeam.equals(localPlayer.getCharacterEntity().getComponent(LASTeamComponent.class).team)) { + gameOverResult.setText("Victory"); + gameOverResult.setFamily("win"); + } else { + gameOverResult.setText("Defeat"); + gameOverResult.setFamily("lose"); + } + } + } + } + @ReceiveEvent public void updateTimer(TimerEvent event, EntityRef entity) { if (localPlayer.getClientEntity().equals(entity)) { @@ -188,4 +246,59 @@ public void notifyOnTooFewPlayers() { "engine:icons#halfGreenHeart"); localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); } + + public void addPlayerStatisticsInfo(DeathScreen deathScreen) { + MigLayout spadesTeamMigLayout = deathScreen.find("spadesTeamPlayerStatistics", MigLayout.class); + MigLayout heartsTeamMigLayout = deathScreen.find("heartsTeamPlayerStatistics", MigLayout.class); + spadesTeamMigLayout.removeAllWidgets(); + heartsTeamMigLayout.removeAllWidgets(); + if (spadesTeamMigLayout != null && heartsTeamMigLayout != null) { + Iterable characters = entityManager.getEntitiesWith(PlayerCharacterComponent.class, + LASTeamComponent.class); + + for (EntityRef character : characters) { + EntityRef client = character.getOwner(); + ClientComponent clientComponent = client.getComponent(ClientComponent.class); + String playerTeam = character.getComponent(LASTeamComponent.class).team; + PlayerStatisticsComponent playerStatisticsComponent = + character.getComponent(PlayerStatisticsComponent.class); + MigLayout migLayout = (playerTeam.equals("black") ? spadesTeamMigLayout : heartsTeamMigLayout); + if (!playerTeam.equals("white")) { + addInfoToTeamMigLayout(migLayout, clientComponent, playerStatisticsComponent); + } + } + } + } + + private void addInfoToTeamMigLayout(MigLayout migLayout, ClientComponent clientComponent, + PlayerStatisticsComponent playerStatisticsComponent) { + migLayout.addWidget(new UILabel(PlayerUtil.getColoredPlayerName(clientComponent.clientInfo)), + new MigLayout.CCHint()); + migLayout.addWidget(new UILabel(String.valueOf(playerStatisticsComponent.kills)), new MigLayout.CCHint()); + migLayout.addWidget(new UILabel(String.valueOf(playerStatisticsComponent.deaths)), new MigLayout.CCHint("wrap" + )); + playerStatisticsComponent.kills = 0; + playerStatisticsComponent.deaths = 0; + } + + private void addFlagInfo(DeathScreen deathScreen, GameOverEvent event) { + addTeamScore(deathScreen, "spadesTeamScore", event.blackTeamScore); + addTeamScore(deathScreen, "heartsTeamScore", event.redTeamScore); + addGoalScore(deathScreen, "spadesGoalScore"); + addGoalScore(deathScreen, "heartsGoalScore"); + } + + public void addTeamScore(DeathScreen deathScreen, String teamUILabelId, int finalScore) { + UILabel teamScore = deathScreen.find(teamUILabelId, UILabel.class); + teamScore.setText(String.valueOf(finalScore)); + } + + public void addGoalScore(DeathScreen deathScreen, String teamUILabelID) { + UILabel goalScore = deathScreen.find(teamUILabelID, UILabel.class); + goalScore.setText(Integer.toString(LASUtils.GOAL_SCORE)); + } + + private void triggerRestart() { + localPlayer.getClientEntity().send(new RestartRequestEvent()); + } } From 470ee3da5dc96738922904cbddd9c978cd741d11 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 23:39:14 +0200 Subject: [PATCH 46/51] refactor: merge ClientRestartSystem into ClientInformationSystem --- .../systems/ClientInformationSystem.java | 16 +++++++ .../systems/ClientRestartSystem.java | 42 ------------------- 2 files changed, 16 insertions(+), 42 deletions(-) delete mode 100644 src/main/java/org/terasology/module/lightandshadow/systems/ClientRestartSystem.java diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java index 832457d0..c479645b 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java @@ -27,6 +27,7 @@ import org.terasology.module.lightandshadow.TapButton; import org.terasology.module.lightandshadow.components.LASConfigComponent; import org.terasology.module.lightandshadow.components.PlayerStatisticsComponent; +import org.terasology.module.lightandshadow.events.ClientRestartEvent; import org.terasology.module.lightandshadow.events.GameOverEvent; import org.terasology.module.lightandshadow.events.RestartRequestEvent; import org.terasology.module.lightandshadow.events.ScoreUpdateFromServerEvent; @@ -40,6 +41,7 @@ import org.terasology.notifications.events.ShowNotificationEvent; import org.terasology.notifications.model.Notification; import org.terasology.notify.ui.DialogNotificationOverlay; +import org.terasology.nui.UILayout; import org.terasology.nui.WidgetUtil; import org.terasology.nui.layouts.miglayout.MigLayout; import org.terasology.nui.widgets.UIButton; @@ -205,6 +207,20 @@ public void run() { } } + @ReceiveEvent + public void onClientRestart(ClientRestartEvent event, EntityRef clientEntity) { + if (localPlayer.getClientEntity().equals(clientEntity)) { + if (nuiManager.isOpen(LASUtils.DEATH_SCREEN)) { + DeathScreen deathScreen = (DeathScreen) nuiManager.getScreen(LASUtils.DEATH_SCREEN); + UILayout migLayout = deathScreen.find("playerStatistics", UILayout.class); + if (migLayout != null) { + migLayout.removeAllWidgets(); + } + nuiManager.closeScreen(LASUtils.DEATH_SCREEN); + } + } + } + @ReceiveEvent public void updateTimer(TimerEvent event, EntityRef entity) { if (localPlayer.getClientEntity().equals(entity)) { diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientRestartSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientRestartSystem.java deleted file mode 100644 index e61ae1a9..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientRestartSystem.java +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.systems; - -import org.terasology.engine.entitySystem.entity.EntityRef; -import org.terasology.engine.entitySystem.systems.BaseComponentSystem; -import org.terasology.engine.entitySystem.systems.RegisterMode; -import org.terasology.engine.entitySystem.systems.RegisterSystem; -import org.terasology.engine.logic.players.LocalPlayer; -import org.terasology.engine.registry.In; -import org.terasology.engine.rendering.nui.NUIManager; -import org.terasology.engine.rendering.nui.layers.ingame.DeathScreen; -import org.terasology.gestalt.entitysystem.event.ReceiveEvent; -import org.terasology.module.lightandshadow.LASUtils; -import org.terasology.module.lightandshadow.events.ClientRestartEvent; -import org.terasology.nui.UILayout; - -/** - * System to close game over screen once restart is complete. - */ - -@RegisterSystem(RegisterMode.CLIENT) -public class ClientRestartSystem extends BaseComponentSystem { - @In - LocalPlayer localPlayer; - @In - NUIManager nuiManager; - - @ReceiveEvent - public void onClientRestart(ClientRestartEvent event, EntityRef clientEntity) { - if (localPlayer.getClientEntity().equals(clientEntity)) { - if (nuiManager.isOpen(LASUtils.DEATH_SCREEN)) { - DeathScreen deathScreen = (DeathScreen) nuiManager.getScreen(LASUtils.DEATH_SCREEN); - UILayout migLayout = deathScreen.find("playerStatistics", UILayout.class); - if (migLayout != null) { - migLayout.removeAllWidgets(); - } - nuiManager.closeScreen(LASUtils.DEATH_SCREEN); - } - } - } -} From eeedd5c26b2d021a8fead8d82b1ecef24cd69011 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 23 Oct 2022 23:43:48 +0200 Subject: [PATCH 47/51] refactor: merge ClientScoreSystem into ClientInformationSystem --- .../systems/ClientInformationSystem.java | 29 +++++++- .../systems/ClientScoreSystem.java | 68 ------------------- 2 files changed, 28 insertions(+), 69 deletions(-) delete mode 100644 src/main/java/org/terasology/module/lightandshadow/systems/ClientScoreSystem.java diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java index c479645b..9bbcb772 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java @@ -32,8 +32,8 @@ import org.terasology.module.lightandshadow.events.RestartRequestEvent; import org.terasology.module.lightandshadow.events.ScoreUpdateFromServerEvent; import org.terasology.module.lightandshadow.events.TimerEvent; -import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseEndedEvent; +import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; import org.terasology.module.lightandshadow.phases.Phase; import org.terasology.module.lightandshadow.phases.SwitchToPhaseEvent; import org.terasology.module.lightandshadow.phases.authority.PhaseSystem; @@ -41,8 +41,10 @@ import org.terasology.notifications.events.ShowNotificationEvent; import org.terasology.notifications.model.Notification; import org.terasology.notify.ui.DialogNotificationOverlay; +import org.terasology.nui.ControlWidget; import org.terasology.nui.UILayout; import org.terasology.nui.WidgetUtil; +import org.terasology.nui.databinding.ReadOnlyBinding; import org.terasology.nui.layouts.miglayout.MigLayout; import org.terasology.nui.widgets.UIButton; import org.terasology.nui.widgets.UILabel; @@ -85,9 +87,34 @@ public class ClientInformationSystem extends BaseComponentSystem { private DialogNotificationOverlay window; + @Override + public void postBegin() { + // Sets score screen bindings + ControlWidget scoreScreen = nuiManager.getHUD().getHUDElement("LightAndShadow:ScoreHud"); + UILabel blackScoreArea = scoreScreen.find("blackScoreArea", UILabel.class); + blackScoreArea.bindText(new ReadOnlyBinding() { + @Override + public String get() { + return String.valueOf(blackScore); + } + }); + UILabel redScoreArea = scoreScreen.find("redScoreArea", UILabel.class); + redScoreArea.bindText(new ReadOnlyBinding() { + @Override + public String get() { + return String.valueOf(redScore); + } + }); + UILabel blackGoalScore = scoreScreen.find("blackGoalScore", UILabel.class); + blackGoalScore.setText(Integer.toString(LASUtils.GOAL_SCORE)); + UILabel redGoalScore = scoreScreen.find("redGoalScore", UILabel.class); + redGoalScore.setText(Integer.toString(LASUtils.GOAL_SCORE)); + } + @Override public void initialise() { window = nuiManager.addOverlay(ASSET_URI, DialogNotificationOverlay.class); + nuiManager.getHUD().addHUDElement("ScoreHud"); deathScreen = nuiManager.createScreen(LASUtils.DEATH_SCREEN, DeathScreen.class); statisticsScreen = nuiManager.createScreen("LightAndShadow:statisticsScreen", DeathScreen.class); addGoalScore(statisticsScreen, "spadesGoalScore"); diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientScoreSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientScoreSystem.java deleted file mode 100644 index b0d66fe2..00000000 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientScoreSystem.java +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2021 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 -package org.terasology.module.lightandshadow.systems; - -import org.terasology.engine.entitySystem.entity.EntityRef; -import org.terasology.engine.entitySystem.systems.BaseComponentSystem; -import org.terasology.engine.entitySystem.systems.RegisterMode; -import org.terasology.engine.entitySystem.systems.RegisterSystem; -import org.terasology.engine.registry.In; -import org.terasology.engine.rendering.nui.NUIManager; -import org.terasology.gestalt.entitysystem.event.ReceiveEvent; -import org.terasology.module.lightandshadow.LASUtils; -import org.terasology.module.lightandshadow.events.ScoreUpdateFromServerEvent; -import org.terasology.nui.ControlWidget; -import org.terasology.nui.databinding.ReadOnlyBinding; -import org.terasology.nui.widgets.UILabel; - -@RegisterSystem(RegisterMode.CLIENT) -public class ClientScoreSystem extends BaseComponentSystem { - @In - private NUIManager nuiManager; - - private int redScore = 0; - private int blackScore = 0; - - @Override - public void postBegin() { - // Sets score screen bindings - ControlWidget scoreScreen = nuiManager.getHUD().getHUDElement("LightAndShadow:ScoreHud"); - UILabel blackScoreArea = scoreScreen.find("blackScoreArea", UILabel.class); - blackScoreArea.bindText(new ReadOnlyBinding() { - @Override - public String get() { - return String.valueOf(blackScore); - } - }); - UILabel redScoreArea = scoreScreen.find("redScoreArea", UILabel.class); - redScoreArea.bindText(new ReadOnlyBinding() { - @Override - public String get() { - return String.valueOf(redScore); - } - }); - UILabel blackGoalScore = scoreScreen.find("blackGoalScore", UILabel.class); - blackGoalScore.setText(Integer.toString(LASUtils.GOAL_SCORE)); - UILabel redGoalScore = scoreScreen.find("redGoalScore", UILabel.class); - redGoalScore.setText(Integer.toString(LASUtils.GOAL_SCORE)); - } - - @Override - public void initialise() { - // Displays score UI to client on game start - nuiManager.getHUD().addHUDElement("ScoreHud"); - } - - @ReceiveEvent - public void onScoreUpdateFromServer(ScoreUpdateFromServerEvent event, EntityRef entity) { - String team = event.team; - if (team.equals(LASUtils.RED_TEAM)) { - redScore = event.score; - return; - } - if (team.equals(LASUtils.BLACK_TEAM)) { - blackScore = event.score; - return; - } - } -} From 5c025a226dc16860276579461b0747424f1b2f70 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Thu, 3 Nov 2022 23:50:27 +0100 Subject: [PATCH 48/51] fix: share TeamSystem to inject it in TeleporterSystem --- .../terasology/module/lightandshadow/systems/TeamSystem.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java index b88b16f1..f73f115a 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java @@ -12,6 +12,7 @@ import org.terasology.engine.entitySystem.systems.RegisterSystem; import org.terasology.engine.network.ClientComponent; import org.terasology.engine.registry.In; +import org.terasology.engine.registry.Share; import org.terasology.gestalt.entitysystem.event.ReceiveEvent; import org.terasology.lightandshadowresources.components.LASTeamComponent; import org.terasology.module.lightandshadow.LASUtils; @@ -19,6 +20,7 @@ import org.terasology.module.lightandshadow.components.LASTeamStatsComponent; @RegisterSystem(RegisterMode.AUTHORITY) +@Share(value = TeamSystem.class) public class TeamSystem extends BaseComponentSystem { @In GameEntitySystem gameEntitySystem; From 171b29af374768a617591f4bebf0f0c8a88322f6 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Thu, 3 Nov 2022 23:51:12 +0100 Subject: [PATCH 49/51] fix: owner of player entity has ClientComponent, not player entity itself --- .../terasology/module/lightandshadow/systems/TeamSystem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java index f73f115a..0ef34f7f 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java @@ -30,7 +30,7 @@ public class TeamSystem extends BaseComponentSystem { @ReceiveEvent(components = LASTeamComponent.class) public void onTeamChange(OnChangedComponent event, EntityRef entity) { logger.debug("Player {} switched to team {}", - entity.getComponent(ClientComponent.class).clientInfo, + entity.getOwner().getComponent(ClientComponent.class).clientInfo, entity.getComponent(LASTeamComponent.class).team); } From 25b7d6ea862581fc454e9ca703f692ba7b95e937 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 6 Nov 2022 23:35:22 +0100 Subject: [PATCH 50/51] refactor: arena exit clean up actions - create PlayerExitedArenaEvent - on arena exit remove AllowShopScreenComponent (players only need the shop in-game) - move check for condition changes that trigger a phase change into LASSystem - switch player to white team instead of removing LASTeam component - trigger arena exit clean up actions for disconnecting clients before they are disconnected (requires https://github.com/MovingBlocks/Terasology/pull/5077) --- .../events/PlayerExitedArenaEvent.java | 19 ++++++++ .../systems/ClientShopSystem.java | 6 +++ .../lightandshadow/systems/LASSystem.java | 35 +++++++++++++++ .../lightandshadow/systems/TeamSystem.java | 13 ++++++ .../systems/TeleporterSystem.java | 43 ++++--------------- 5 files changed, 82 insertions(+), 34 deletions(-) create mode 100644 src/main/java/org/terasology/module/lightandshadow/events/PlayerExitedArenaEvent.java diff --git a/src/main/java/org/terasology/module/lightandshadow/events/PlayerExitedArenaEvent.java b/src/main/java/org/terasology/module/lightandshadow/events/PlayerExitedArenaEvent.java new file mode 100644 index 00000000..bfa70896 --- /dev/null +++ b/src/main/java/org/terasology/module/lightandshadow/events/PlayerExitedArenaEvent.java @@ -0,0 +1,19 @@ +// Copyright 2022 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package org.terasology.module.lightandshadow.events; + +import org.terasology.engine.entitySystem.entity.EntityRef; +import org.terasology.gestalt.entitysystem.event.Event; + +public class PlayerExitedArenaEvent implements Event { + private final EntityRef player; + + public PlayerExitedArenaEvent(EntityRef player) { + this.player = player; + } + + public EntityRef getPlayer() { + return player; + } +} diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientShopSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientShopSystem.java index 177fd928..04c647e0 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientShopSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientShopSystem.java @@ -20,6 +20,7 @@ import org.terasology.input.ButtonState; import org.terasology.module.inventory.input.InventoryButton; import org.terasology.module.lightandshadow.LASUtils; +import org.terasology.module.lightandshadow.events.PlayerExitedArenaEvent; import org.terasology.module.lightandshadow.phases.OnPreGamePhaseStartedEvent; import org.terasology.notifications.events.ExpireNotificationEvent; import org.terasology.notifications.events.ShowNotificationEvent; @@ -43,6 +44,11 @@ public void onPregameStart(OnPreGamePhaseStartedEvent event, EntityRef entity) { entity.upsertComponent(AllowShopScreenComponent.class, c -> c.orElse(new AllowShopScreenComponent())); } + @ReceiveEvent + public void onArenaExit(PlayerExitedArenaEvent event, EntityRef entity) { + entity.removeComponent(AllowShopScreenComponent.class); + } + /** * Handles the button event if in-game shop is enabled. * Needs to have a higher priority than {@link MarketUiClientSystem#onToggleInventory(InventoryButton, EntityRef)} diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/LASSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/LASSystem.java index 6e13f2b7..cf765b92 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/LASSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/LASSystem.java @@ -3,6 +3,8 @@ package org.terasology.module.lightandshadow.systems; import org.joml.Vector3f; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.terasology.economy.components.CurrencyStorageComponent; import org.terasology.economy.events.WalletUpdatedEvent; import org.terasology.engine.entitySystem.entity.EntityRef; @@ -12,6 +14,7 @@ import org.terasology.engine.entitySystem.systems.RegisterSystem; import org.terasology.engine.logic.characters.CharacterTeleportEvent; import org.terasology.engine.logic.players.event.OnPlayerSpawnedEvent; +import org.terasology.engine.network.ClientComponent; import org.terasology.engine.registry.In; import org.terasology.engine.world.sun.CelestialSystem; import org.terasology.gestalt.entitysystem.event.ReceiveEvent; @@ -19,6 +22,10 @@ import org.terasology.module.inventory.events.RemoveItemAction; import org.terasology.module.inventory.systems.InventoryManager; import org.terasology.module.lightandshadow.LASUtils; +import org.terasology.module.lightandshadow.events.PlayerExitedArenaEvent; +import org.terasology.module.lightandshadow.phases.Phase; +import org.terasology.module.lightandshadow.phases.SwitchToPhaseEvent; +import org.terasology.module.lightandshadow.phases.authority.PhaseSystem; @RegisterSystem public class LASSystem extends BaseComponentSystem { @@ -28,6 +35,12 @@ public class LASSystem extends BaseComponentSystem { private CelestialSystem celestialSystem; @In private GameEntitySystem gameEntitySystem; + @In + private PhaseSystem phaseSystem; + @In + private TeamSystem teamSystem; + + private static final Logger logger = LoggerFactory.getLogger(LASSystem.class); /** * Gives an empty inventory to a player in the lobby to prevent fight's in the lobby and gives the player some funds. @@ -45,6 +58,28 @@ public void onPlayerSpawn(OnPlayerSpawnedEvent event, EntityRef player, Inventor player.send(new CharacterTeleportEvent(new Vector3f(LASUtils.FLOATING_PLATFORM_POSITION).add(0, 1, 0))); } + /** + * Follow-up actions necessary when a player leaves the arena, either by porting back to the platform + * or by disconnecting from the server. + * These action are required to verify the current game state and update the phase accordingly if necessary. + */ + @ReceiveEvent + public void onArenaExit(PlayerExitedArenaEvent event, EntityRef player) { + // if in game phase: verify that game start condition still met + Phase currentPhase = phaseSystem.getCurrentPhase(); + if ((currentPhase == Phase.IN_GAME || currentPhase == Phase.COUNTDOWN) && !teamSystem.isMinSizeTeams()) { + logger.debug("Starting condition no longer met, switching from phase {} to PRE_GAME", currentPhase); + gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.PRE_GAME)); + } + + // if the player teleporting was the last one still in the arena + // switch back to idle phase + if (teamSystem.getTeamSize(LASUtils.BLACK_TEAM) == 0 && teamSystem.getTeamSize(LASUtils.RED_TEAM) == 0) { + logger.debug("No players left in arena, switching to IDLE phase"); + gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.IDLE)); + } + } + @Override public void initialise() { if (!celestialSystem.isSunHalted()) { diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java index 0ef34f7f..7bace3e9 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeamSystem.java @@ -7,6 +7,8 @@ import org.slf4j.LoggerFactory; import org.terasology.engine.entitySystem.entity.EntityRef; import org.terasology.engine.entitySystem.entity.lifecycleEvents.OnChangedComponent; +import org.terasology.engine.entitySystem.event.EventPriority; +import org.terasology.engine.entitySystem.event.Priority; import org.terasology.engine.entitySystem.systems.BaseComponentSystem; import org.terasology.engine.entitySystem.systems.RegisterMode; import org.terasology.engine.entitySystem.systems.RegisterSystem; @@ -18,6 +20,7 @@ import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.components.LASConfigComponent; import org.terasology.module.lightandshadow.components.LASTeamStatsComponent; +import org.terasology.module.lightandshadow.events.PlayerExitedArenaEvent; @RegisterSystem(RegisterMode.AUTHORITY) @Share(value = TeamSystem.class) @@ -34,6 +37,16 @@ public void onTeamChange(OnChangedComponent event, EntityRef entity) { entity.getComponent(LASTeamComponent.class).team); } + @Priority(EventPriority.PRIORITY_HIGH) + @ReceiveEvent(components = LASTeamComponent.class) + public void onArenaExit(PlayerExitedArenaEvent event, EntityRef player) { + // players exiting the arena (e.g. by teleporting to platform or disconnecting from the game) + // should be removed from the playing teams + LASTeamComponent lasTeamComponent = player.getComponent(LASTeamComponent.class); + lasTeamComponent.team = LASUtils.WHITE_TEAM; + player.addOrSaveComponent(lasTeamComponent); + } + public boolean isBalancedTeams(String targetTeam) { gameEntitySystem.updateTeamStats(); EntityRef gameEntity = gameEntitySystem.getGameEntity(); diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java index 0b8b5dae..8dffad51 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/TeleporterSystem.java @@ -20,7 +20,7 @@ import org.terasology.engine.logic.permission.PermissionManager; import org.terasology.engine.logic.players.SetDirectionEvent; import org.terasology.engine.network.ClientComponent; -import org.terasology.engine.network.events.DisconnectedEvent; +import org.terasology.engine.network.events.BeforeDisconnectEvent; import org.terasology.engine.registry.In; import org.terasology.engine.utilities.Assets; import org.terasology.gestalt.entitysystem.event.Event; @@ -31,6 +31,7 @@ import org.terasology.module.inventory.events.RequestInventoryEvent; import org.terasology.module.lightandshadow.LASUtils; import org.terasology.module.lightandshadow.components.LASConfigComponent; +import org.terasology.module.lightandshadow.events.PlayerExitedArenaEvent; import org.terasology.module.lightandshadow.events.TimerEvent; import org.terasology.module.lightandshadow.phases.Phase; import org.terasology.module.lightandshadow.phases.SwitchToPhaseEvent; @@ -40,8 +41,8 @@ import java.util.function.Supplier; /** - * Teleports players to play arena once they chose their team. - * It also sends events to change players skins and hud based on team they have chosen. + * Teleports players to play arena once they chose their team. It also sends events to change players skins and hud based on team they have + * chosen. * * @see ClientSkinSystem */ @@ -64,9 +65,8 @@ public class TeleporterSystem extends BaseComponentSystem { private final Random random = new Random(); /** - * Depending on which teleporter the player chooses, they are set to that team - * and teleported to that base - * Assumption: there are two teleporters, one for the red, one for the black team + * Depending on which teleporter the player chooses, they are set to that team and teleported to that base Assumption: there are two + * teleporters, one for the red, one for the black team * * @param event * @param entity @@ -127,44 +127,19 @@ public String teleportToPlatform(@Sender EntityRef sender) { LASTeamComponent senderTeam = clientComp.character.getComponent(LASTeamComponent.class); if (senderTeam != null && (senderTeam.team.equals(LASUtils.RED_TEAM) || senderTeam.team.equals(LASUtils.BLACK_TEAM))) { // spectators (white team) are not relevant for arena exit actions - performArenaExitActions(clientComp); + clientComp.character.send(new PlayerExitedArenaEvent(sender)); } clientComp.character.send(new CharacterTeleportEvent(new Vector3f(LASUtils.FLOATING_PLATFORM_POSITION).add(0, 1, 0))); return "Teleporting you to the platform."; } @ReceiveEvent(components = ClientComponent.class) - public void onPlayerDisconnect(DisconnectedEvent event, EntityRef entity) { + public void onPlayerDisconnect(BeforeDisconnectEvent event, EntityRef entity) { ClientComponent clientComp = entity.getComponent(ClientComponent.class); LASTeamComponent senderTeam = clientComp.character.getComponent(LASTeamComponent.class); if (senderTeam != null && (senderTeam.team.equals(LASUtils.RED_TEAM) || senderTeam.team.equals(LASUtils.BLACK_TEAM))) { // spectators (white team) are not relevant for arena exit actions - performArenaExitActions(clientComp); - } - } - - /** - * Follow-up actions necessary when a player leaves the arena, either by porting back to the platform - * or by disconnecting from the server. - * These action are required to verify the current game state and update the phase accordingly if necessary. - */ - private void performArenaExitActions(ClientComponent clientComp) { - // players teleporting back to platform should be removed from the playing teams - // white team (spectator) members are allowed to teleport without losing their team - clientComp.character.removeComponent(LASTeamComponent.class); - - // if in game phase: verify that game start condition still met - Phase currentPhase = phaseSystem.getCurrentPhase(); - if ((currentPhase == Phase.IN_GAME || currentPhase == Phase.COUNTDOWN) && !teamSystem.isMinSizeTeams()) { - logger.debug("Starting condition no longer met, switching from phase {} to PRE_GAME", currentPhase); - gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.PRE_GAME)); - } - - // if the player teleporting was the last one still in the arena - // switch back to idle phase - if (teamSystem.getTeamSize(LASUtils.BLACK_TEAM) == 0 && teamSystem.getTeamSize(LASUtils.RED_TEAM) == 0) { - logger.debug("No players left in arena, switching to IDLE phase"); - gameEntitySystem.getGameEntity().send(new SwitchToPhaseEvent(Phase.IDLE)); + teleportToPlatform(entity); } } } From d93ea07df696b453f9bfd8ac647d304e4c365434 Mon Sep 17 00:00:00 2001 From: Josephine Rueckert Date: Sun, 6 Nov 2022 23:42:24 +0100 Subject: [PATCH 51/51] feat: expire "too few players" notification when no more players in arena --- .../lightandshadow/systems/ClientInformationSystem.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java index 9bbcb772..150b786c 100644 --- a/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java +++ b/src/main/java/org/terasology/module/lightandshadow/systems/ClientInformationSystem.java @@ -290,6 +290,11 @@ public void notifyOnTooFewPlayers() { localPlayer.getClientEntity().send(new ShowNotificationEvent(notification)); } + @ReceiveEvent + public void removeTooFewPlayersNotification(OnPreGamePhaseEndedEvent event, EntityRef entity) { + entity.send(new ExpireNotificationEvent(WAIT_NOTIFICATION_ID)); + } + public void addPlayerStatisticsInfo(DeathScreen deathScreen) { MigLayout spadesTeamMigLayout = deathScreen.find("spadesTeamPlayerStatistics", MigLayout.class); MigLayout heartsTeamMigLayout = deathScreen.find("heartsTeamPlayerStatistics", MigLayout.class);