Skip to content

Commit

Permalink
🚑 Fix bugs with Entities enter on map
Browse files Browse the repository at this point in the history
  • Loading branch information
ImNotAVirus committed Sep 24, 2023
1 parent 9030ec8 commit 2f65633
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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} =
Expand Down
40 changes: 20 additions & 20 deletions apps/channel_service/lib/channel_service/endpoint/protocol.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions apps/game_service/lib/game_service.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
48 changes: 28 additions & 20 deletions apps/game_service/lib/game_service/systems/entity_visibility.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down

0 comments on commit 2f65633

Please sign in to comment.