Skip to content

Commit

Permalink
♻️ Refacto the player disconnection event
Browse files Browse the repository at this point in the history
  • Loading branch information
ImNotAVirus committed Sep 22, 2023
1 parent 584a3a1 commit 8bf4b1a
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 26 deletions.
23 changes: 5 additions & 18 deletions apps/channel_service/lib/channel_service/presence_manager.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ defmodule ChannelService.PresenceManager do
alias ElvenCaching.SessionRegistry

alias ElvenGard.ECS.{Command, Entity, Query}
alias GameService.Events.EntityDespawned
alias GameService.Events.PlayerDisconnected
alias GameService.EntityComponents.PositionComponent
alias GameService.PlayerComponents.AccountComponent

Expand Down Expand Up @@ -77,27 +77,14 @@ defmodule ChannelService.PresenceManager do
defp cleanup_session(%Session{state: :in_lobby}), do: :ok

defp cleanup_session(%Session{state: :in_game, account_id: account_id}) do
# Not so clean but I'll rewrite this part later
{entity, components} =
Entity
|> Query.select(
with: [{AccountComponent, [{:==, :id, account_id}]}],
preload: [PositionComponent]
)
|> Query.one()

%PositionComponent{map_ref: map_ref} =
position = Enum.find(components, &(&1.__struct__ == PositionComponent))

# Remove Entity from ECS and notify all Frontends
{:ok, _events} =
ElvenGard.ECS.push(
# Here we only need the position component for the despawn event
%EntityDespawned{entity: entity, components: [position]},
partition: map_ref
%PlayerDisconnected{account_id: account_id},
# Here we don't know the map_ref so we will send the event to a special partition
partition: :system
)

{:ok, _tuple} = Command.despawn_entity(entity)

# TODO: Save character in db
# ...
end
Expand Down
1 change: 1 addition & 0 deletions apps/game_service/lib/game_service/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ defmodule GameService.Application do
{ElvenGard.Cluster.MnesiaClusterManager, []},
{ElvenGard.ECS.Topology.EventSource, [hash: partition_hash]},
# FIXME: Later rewrite partitions with a DynamicSupervisor
{GameService.SystemPartition, []},
{GameService.StaticMapPartition, [id: 1]}
]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule GameService.Events.ChangeDirection do
defmodule GameService.Events.EntityChangeDirection do
@moduledoc """
Event triggered when an Entity change his direction.
"""
Expand Down
11 changes: 11 additions & 0 deletions apps/game_service/lib/game_service/events/player_disconnected.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule GameService.Events.PlayerDisconnected do
@moduledoc """
Event triggered when a player disconnect.
This event is responsible for cleaning the state and send a notification to clients
"""

use ElvenGard.ECS.Event, fields: [:account_id]

@type t :: %__MODULE__{account_id: non_neg_integer()}
end
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ defmodule GameService.StaticMapPartition do
def setup(opts) do
id = opts[:id] || raise ArgumentError, ":id option is require for a map"

Logger.debug("StaticMapPartition id: #{id} Starting...")
Logger.debug("StaticMapPartition id: #{id} is starting...")

# Run system 60 per seconds (60Hz)
interval = trunc(1 / 60 * 1000)
Expand Down
29 changes: 29 additions & 0 deletions apps/game_service/lib/game_service/partitions/system_partition.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
defmodule GameService.SystemPartition do
@moduledoc """
TODO: Documentation for GameService.SystemPartition
"""

use ElvenGard.ECS.Topology.Partition

require Logger

# Partition behaviour

@impl true
def setup(_opts) do
Logger.debug("SystemPartition is starting...")

# Run system 60 per seconds (60Hz)
interval = trunc(1 / 60 * 1000)

{:system, systems: systems(), interval: interval}
end

# Private functions

defp systems() do
[
GameService.SessionDisconnectionSystem
]
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ defmodule GameService.EntityMapActionsSystem do
GameService.EntityComponents.SittingComponent
],
event_subscriptions: [
GameService.Events.ChangeDirection,
GameService.Events.EntityChangeDirection,
GameService.Events.EntityInfoRequest,
GameService.Events.EntityMove,
GameService.Events.EntitySit
Expand All @@ -28,7 +28,7 @@ defmodule GameService.EntityMapActionsSystem do
alias GameService.EntityComponents, as: E

alias GameService.Events.{
ChangeDirection
EntityChangeDirection
# EntityInfoRequest,
# Movement,
# Sitting
Expand All @@ -37,8 +37,8 @@ defmodule GameService.EntityMapActionsSystem do
# System behaviour

@impl true
def run(%ChangeDirection{} = event, _delta) do
%ChangeDirection{
def run(%EntityChangeDirection{} = event, _delta) do
%EntityChangeDirection{
entity_type: entity_type,
entity_id: entity_id,
value: value
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
defmodule GameService.SessionDisconnectionSystem do
@moduledoc """
TODO: Documentation for GameService.SessionDisconnectionSystem
FIXME: Test this system
"""

use ElvenGard.ECS.System,
lock_components: :sync,
event_subscriptions: [
GameService.Events.PlayerDisconnected
]

require Logger

alias ElvenGard.ECS.{Command, Query}

alias GameService.EntityComponents, as: E
alias GameService.Events.{EntityDespawned, PlayerDisconnected}

# System behaviour

@impl true
def run(%PlayerDisconnected{account_id: account_id}, _delta) do
# Get the disconnected Entity and his PositionComponent
{entity, components} =
Entity
|> Query.select(
with: [{AccountComponent, [{:==, :id, account_id}]}],
preload: [PositionComponent]
)
|> Query.one()

# Find the PositionComponent
%E.PositionComponent{map_ref: map_ref} =
position = Enum.find(components, &(&1.__struct__ == E.PositionComponent))

# Send the EntityDespawned event to notify player
# FIXME: Later rewrite this part:
# - EntityDespawned should be renamed EntityLeaveMap
# - attrs must be only entity_id and map_ref
{:ok, _events} =
ElvenGard.ECS.push(
# Here we only need the position component for the despawn event
%EntityDespawned{entity: entity, components: [position]},
partition: map_ref
)

# Remove the Entity from our systems
{:ok, _tuple} = Command.despawn_entity(entity)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ defmodule GameService.EntityMapActionsSystemTest do
direction = %E.DirectionComponent{value: :south}
entity = spawn_player(components: [position, direction])

# Call our System with a ChangeDirection event
event = %Evt.ChangeDirection{
# Call our System with a EntityChangeDirection event
event = %Evt.EntityChangeDirection{
entity_type: :player,
entity_id: GameService.entity_id(entity),
value: :north
Expand Down

0 comments on commit 8bf4b1a

Please sign in to comment.