diff --git a/apps/channel_service/lib/channel_service/actions/game_actions.ex b/apps/channel_service/lib/channel_service/actions/game_actions.ex index 22c9f047..9b6243c6 100644 --- a/apps/channel_service/lib/channel_service/actions/game_actions.ex +++ b/apps/channel_service/lib/channel_service/actions/game_actions.ex @@ -36,7 +36,7 @@ defmodule ChannelService.GameActions do # TODO: Socket.send(socket, InventoryViews.render(:qslot, %{slot_id: 1, entity: bundle})) Socket.send(socket, UIViews.render(:info, %{message: "Welcome to my World!"})) send_bns(socket) - send_hello(socket, player) + send_hello(socket, bundle) # Send an entity map enter event to the map partition {:ok, _events} = diff --git a/apps/channel_service/lib/channel_service/endpoint/protocol.ex b/apps/channel_service/lib/channel_service/endpoint/protocol.ex index 2c2a3086..4c4ccfe1 100644 --- a/apps/channel_service/lib/channel_service/endpoint/protocol.ex +++ b/apps/channel_service/lib/channel_service/endpoint/protocol.ex @@ -45,27 +45,27 @@ defmodule ChannelService.Endpoint.Protocol do ## GenServer behaviour @impl true + def handle_info({:map_change, %PlayerBundle{} = entity}, socket) do + entity_args = %{entity: entity} + stat_args = %{entity: entity, option: 0} + at_args = %{entity: entity, map_music: 1} + + Socket.send(socket, PlayerViews.render(:c_info, entity_args)) + Socket.send(socket, PlayerViews.render(:lev, entity_args)) + Socket.send(socket, PlayerViews.render(:stat, stat_args)) + Socket.send(socket, MapViews.render(:at, at_args)) + Socket.send(socket, MapViews.render(:c_map, entity_args)) + # TODO: Socket.send(socket, PlayerViews.render(:sc, entity_args)) + Socket.send(socket, EntityViews.render(:c_mode, entity_args)) + Socket.send(socket, EntityViews.render(:char_sc, entity_args)) + Socket.send(socket, EntityViews.render(:cond, entity_args)) + + {:noreply, socket} + end + def handle_info({:entity_map_enter, %PlayerBundle{} = entity}, socket) do - case entity.id == socket.assigns.character_id do - false -> - Socket.send(socket, VisibilityViews.render(:in, %{entity: entity})) - Socket.send(socket, EntityViews.render(:c_mode, %{entity: entity})) - - true -> - entity_args = %{entity: entity} - stat_args = %{entity: entity, option: 0} - at_args = %{entity: entity, map_music: 1} - - Socket.send(socket, PlayerViews.render(:c_info, entity_args)) - Socket.send(socket, PlayerViews.render(:lev, entity_args)) - Socket.send(socket, PlayerViews.render(:stat, stat_args)) - Socket.send(socket, MapViews.render(:at, at_args)) - Socket.send(socket, MapViews.render(:c_map, entity_args)) - # TODO: Socket.send(socket, PlayerViews.render(:sc, entity_args)) - Socket.send(socket, EntityViews.render(:c_mode, entity_args)) - Socket.send(socket, EntityViews.render(:char_sc, entity_args)) - Socket.send(socket, EntityViews.render(:cond, entity_args)) - end + Socket.send(socket, VisibilityViews.render(:in, %{entity: entity})) + Socket.send(socket, EntityViews.render(:c_mode, %{entity: entity})) {:noreply, socket} end diff --git a/apps/game_service/lib/game_service.ex b/apps/game_service/lib/game_service.ex index 0638e16a..008e7e22 100644 --- a/apps/game_service/lib/game_service.ex +++ b/apps/game_service/lib/game_service.ex @@ -24,6 +24,14 @@ defmodule GameService do PlayerBundle.preload(entity, components) end + def send_to(maybe_events, %EndpointComponent{pid: pid}) do + events = List.wrap(maybe_events) + + for event <- events do + send(pid, event) + end + end + def broadcast_to(maybe_events, maybe_endpoints) do events = List.wrap(maybe_events) endpoints = List.wrap(maybe_endpoints) diff --git a/apps/game_service/lib/game_service/systems/entity_visibility.ex b/apps/game_service/lib/game_service/systems/entity_visibility.ex index c5efcf4a..2a6234b9 100644 --- a/apps/game_service/lib/game_service/systems/entity_visibility.ex +++ b/apps/game_service/lib/game_service/systems/entity_visibility.ex @@ -23,19 +23,24 @@ defmodule GameService.EntityVisibilitySystem do {:ok, entity} = Query.fetch_entity(ecs_id) {:ok, position} = Query.fetch_component(entity, E.PositionComponent) - # Get all Entities with all Components on the map - entities = - ElvenGard.ECS.Entity - |> Query.select( - with: [{E.PositionComponent, [{:==, :map_ref, position.map_ref}]}], - preload: :all - ) - |> Query.all() + # Create the bundle + {:ok, components} = ElvenGard.ECS.Query.list_components(entity) + bundle = GameService.load_bundle(entity, components) - # Send Events - Enum.each(entities, fn {entity, components} -> - _ = broadcast_event(:entity_map_enter, entity, components, position) - end) + # Notify all Endpoint on the same map except ourself + GameService.System.map_event({:entity_map_enter, bundle}, position, [entity]) + + # If the Entity has an EndpointComponent, notify the map change and + # send him all entities on the map + with {:ok, endpoint} <- Query.fetch_component(entity, P.EndpointComponent) do + _ = GameService.send_to({:map_change, bundle}, endpoint) + + position + |> list_map_bundles() + |> Enum.reject(&(&1.id == entity_id)) + |> Enum.map(&{:entity_map_enter, &1}) + |> GameService.send_to(endpoint) + end end def run(%EntityDespawned{entity: entity, components: components}, _delta) do @@ -46,14 +51,6 @@ defmodule GameService.EntityVisibilitySystem do ## Helpers - defp broadcast_event(event_name, entity, components, %E.PositionComponent{} = position) do - # Transform the entity + components to a bundle - bundle = GameService.load_bundle(entity, components) - - # Send Events - GameService.System.map_event({event_name, bundle}, position) - end - # FIXME: Remove the EntityDespawned defp broadcast_event2(event_name, entity, components, %E.PositionComponent{} = position) do # Transform the entity + components to a bundle @@ -62,4 +59,15 @@ defmodule GameService.EntityVisibilitySystem do # Send Events GameService.System.map_event({event_name, bundle}, position) end + + defp list_map_bundles(position) do + # Get all Entities with all Components on the map + ElvenGard.ECS.Entity + |> Query.select( + with: [{E.PositionComponent, [{:==, :map_ref, position.map_ref}]}], + preload: :all + ) + |> Query.all() + |> Enum.map(&GameService.load_bundle(elem(&1, 0), elem(&1, 1))) + end end diff --git a/apps/game_service/test/game_service/systems/entity_visibility_system_test.exs b/apps/game_service/test/game_service/systems/entity_visibility_system_test.exs index ba5fd2ea..13d58f2b 100644 --- a/apps/game_service/test/game_service/systems/entity_visibility_system_test.exs +++ b/apps/game_service/test/game_service/systems/entity_visibility_system_test.exs @@ -24,6 +24,22 @@ defmodule GameService.EntityVisibilitySystemTest do assert bundle.position == position end + test "system notify on map change" do + # Register our process to receive message + ref = make_ref() + position = %E.PositionComponent{map_ref: ref} + endpoint = %P.EndpointComponent{pid: self()} + %Entity{id: {type, id}} = spawn_player(components: [endpoint, position]) + + # Call our System with a EntityMapEnter event + event = %Evt.EntityMapEnter{entity_type: type, entity_id: id} + _ = EntityVisibilitySystem.run(event, 0) + + # We should receive an event with a bundle + assert_receive {:map_change, %PlayerBundle{id: ^id} = bundle} + assert bundle.position == position + end + test "send to the new Entity others that are on the map" do # Register some dummies ref = make_ref() @@ -38,7 +54,7 @@ defmodule GameService.EntityVisibilitySystemTest do _ = EntityVisibilitySystem.run(event, 0) # We should receive events with our old Entities - assert_receive {:entity_map_enter, %PlayerBundle{id: ^id}} + refute_receive {:entity_map_enter, %PlayerBundle{id: ^id}} assert_receive {:entity_map_enter, %PlayerBundle{id: ^id1}} assert_receive {:entity_map_enter, %PlayerBundle{id: ^id2}} end