Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Event bus #72

Merged
merged 2 commits into from Aug 25, 2018
Merged

Event bus #72

Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.
+101 −109
Diff settings

Always

Just for now

Copy path View file
@@ -11,8 +11,8 @@ defmodule Game.Room do
alias Game.Environment
alias Game.Features
alias Game.Items
alias Game.NPC
alias Game.Room.Actions
alias Game.Room.EventBus
alias Game.Room.Repo
alias Game.Session
alias Game.World.Master, as: WorldMaster
@@ -161,65 +161,37 @@ defmodule Game.Room do
end

def handle_cast({:enter, {:user, user}, reason}, state) do
%{room: room, players: players, npcs: npcs} = state

%{room: room, players: players} = state
Logger.debug(fn -> "Player (#{user.id}) entered room (#{room.id})" end, type: :room)

players |> inform_players({"room/entered", {{:user, user}, reason}})
npcs |> inform_npcs({"room/entered", {{:user, user}, reason}})

{:noreply, Map.put(state, :players, [user | players])}
state = %{state | players: [user | players]}
handle_cast({:notify, {:user, user}, {"room/entered", {{:user, user}, reason}}}, state)
end

def handle_cast({:enter, {:npc, npc}, reason}, state) do
%{room: room, players: players, npcs: npcs} = state

%{room: room, npcs: npcs} = state
Logger.debug(fn -> "NPC (#{npc.id}) entered room (#{room.id})" end, type: :room)

players |> inform_players({"room/entered", {{:npc, npc}, reason}})
npcs |> inform_npcs({"room/entered", {{:npc, npc}, reason}})

{:noreply, Map.put(state, :npcs, [npc | npcs])}
state = %{state | npcs: [npc | npcs]}
handle_cast({:notify, {:npc, npc}, {"room/entered", {{:npc, npc}, reason}}}, state)
end

def handle_cast({:leave, {:user, user}, reason}, state) do
%{room: room, players: players} = state

Logger.debug(fn -> "Player (#{user.id}) left room (#{room.id})" end, type: :room)
players = Enum.reject(players, &(&1.id == user.id))
state = %{state | players: players}

handle_cast({:notify, {:user, user}, {"room/leave", {{:user, user}, reason}}}, state)
end

def handle_cast({:leave, {:npc, npc}, reason}, state) do
%{room: room, npcs: npcs} = state

Logger.debug(fn -> "NPC (#{npc.id}) left room (#{room.id})" end, type: :room)
npcs = Enum.reject(npcs, &(&1.id == npc.id))
state = %{state | npcs: npcs}

handle_cast({:notify, {:npc, npc}, {"room/leave", {{:npc, npc}, reason}}}, state)
end

def handle_cast({:notify, {:user, sender}, event}, state = %{players: players, npcs: npcs}) do
# don't send to the sender
players
|> Enum.reject(&(&1.id == sender.id))
|> inform_players(event)

npcs |> inform_npcs(event)

{:noreply, state}
end

def handle_cast({:notify, {:npc, sender}, event}, state = %{players: players, npcs: npcs}) do
players |> inform_players(event)

# don't send to the sender
npcs
|> Enum.reject(&(&1.id == sender.id))
|> inform_npcs(event)
def handle_cast({:notify, actor, event}, state) do
EventBus.notify(state.room.id, actor, event, state.players, state.npcs)

{:noreply, state}
end
@@ -307,11 +279,4 @@ defmodule Game.Room do
Session.notify(user, action)
end)
end

@spec inform_npcs(npcs :: list, action :: tuple) :: :ok
defp inform_npcs(npcs, action) do
Enum.each(npcs, fn npc ->
NPC.notify(npc.id, action)
end)
end
end
Copy path View file
@@ -0,0 +1,75 @@
defmodule Game.Room.EventBus do
@moduledoc """
A side process for rooms that notify characters
Notifying in the main room process was too consuming, so do it in a side process
"""

use GenServer

alias Game.NPC
alias Game.Room
alias Game.Session

def start_link(room_id) do
GenServer.start_link(__MODULE__, room_id, name: pid(room_id), id: "#{room_id}-notify")
end

def pid(id) do
{:global, {Game.Room.EventBus, id}}
end

def notify(room_id, actor, event, players, npcs) do
GenServer.cast(pid(room_id), {:notify, actor, event, players, npcs})
end

def init(room_id) do
{:ok, %{room_id: room_id}, {:continue, :link}}
end

def handle_continue(:link, state) do
case :global.whereis_name({Room, state.room_id}) do
:undefined ->
{:stop, :normal, state}

pid ->
Process.link(pid)
{:noreply, state}
end
end

def handle_cast({:notify, {:user, sender}, event, players, npcs}, state) do
# don't send to the sender
players
|> Enum.reject(&(&1.id == sender.id))
|> inform_players(event)

npcs |> inform_npcs(event)

{:noreply, state}
end

def handle_cast({:notify, {:npc, sender}, event, players, npcs}, state) do
players |> inform_players(event)

# don't send to the sender
npcs
|> Enum.reject(&(&1.id == sender.id))
|> inform_npcs(event)

{:noreply, state}
end

defp inform_players(players, action) do
Enum.each(players, fn user ->
Session.notify(user, action)
end)
end

@spec inform_npcs(npcs :: list, action :: tuple) :: :ok
defp inform_npcs(npcs, action) do
Enum.each(npcs, fn npc ->
NPC.notify(npc.id, action)
end)
end
end
Copy path View file
@@ -31,12 +31,20 @@ defmodule Game.Room.Supervisor do
Supervisor.start_child(pid, child_spec)
end

def start_bus(pid, room) do
child_spec = worker(Room.EventBus, [room.id], id: "#{room.id}-notify", restart: :permanent)
Supervisor.start_child(pid, child_spec)
end

def init(zone) do
children =
zone.id
|> Room.for_zone()
|> Enum.map(fn room_id ->
worker(Room, [room_id], id: room_id, restart: :permanent)
|> Enum.flat_map(fn room_id ->
[
worker(Room, [room_id], id: room_id, restart: :permanent),
worker(Room.EventBus, [room_id], id: "#{room_id}-notify", restart: :permanent),
]
end)

Zone.room_supervisor(zone.id, self())
Copy path View file
@@ -298,6 +298,7 @@ defmodule Game.Zone do

def handle_cast({:spawn_room, room}, state = %{room_supervisor_pid: room_supervisor_pid}) do
Room.Supervisor.start_child(room_supervisor_pid, room)
Room.Supervisor.start_bus(room_supervisor_pid, room)
{:noreply, state}
end

Copy path View file
@@ -2,9 +2,7 @@ defmodule Game.RoomTest do
use Data.ModelCase

alias Data.User
alias Game.Message
alias Game.Room
alias Game.Session

setup do
{:ok, user: %{id: 10, name: "user"}, room: %{id: 11}}
@@ -18,29 +16,6 @@ defmodule Game.RoomTest do

assert state.players == [user]
end

test "entering a room sends notifications - user", %{user: user, room: room} do
notify_user = %User{id: 11}
Session.Registry.register(notify_user)

state = %{room: room, players: [notify_user], npcs: []}

{:noreply, _state} = Room.handle_cast({:enter, {:user, user}, :enter}, state)

assert_receive {:"$gen_cast", {:notify, {"room/entered", {{:user, ^user}, :enter}}}}
end

test "entering a room sends notifications - npc", %{room: room} do
notify_user = %User{id: 11}
Session.Registry.register(notify_user)

npc = %{id: 10, name: "Bandit"}

state = %{room: room, players: [notify_user], npcs: []}
{:noreply, _state} = Room.handle_cast({:enter, {:npc, npc}, :enter}, state)

assert_receive {:"$gen_cast", {:notify, {"room/entered", {{:npc, ^npc}, :enter}}}}
end
end

test "leaving a room - user", %{user: user, room: room} do
@@ -51,47 +26,12 @@ defmodule Game.RoomTest do
assert state.players == []
end

test "leaving a room sends notifications - user", %{user: user, room: room} do
notify_user = %User{id: 11}
Session.Registry.register(notify_user)

state = %{room: room, players: [notify_user], npcs: []}

{:noreply, _state} = Room.handle_cast({:leave, {:user, user}, :leave}, state)

assert_receive {:"$gen_cast", {:notify, {"room/leave", {{:user, ^user}, :leave}}}}
end

test "leaving a room sends notifications - npc", %{room: room} do
notify_user = %User{id: 11}
Session.Registry.register(notify_user)

npc = %{id: 10, name: "Bandit"}
state = %{room: room, npcs: [npc], players: [notify_user]}

{:noreply, _state} = Room.handle_cast({:leave, {:npc, npc}, :leave}, state)

assert_receive {:"$gen_cast", {:notify, {"room/leave", {{:npc, ^npc}, :leave}}}}
end

test "leaving a room - npc", %{room: room} do
npc = %{id: 10, name: "Bandit"}
{:noreply, state} = Room.handle_cast({:leave, {:npc, npc}, :leave}, %{room: room, npcs: [npc], players: []})
assert state.npcs == []
end

test "emoting", %{user: user} do
notify_user = %User{id: 11}
Session.Registry.register(notify_user)

message = Message.emote(user, "emote")
state = %{players: [notify_user], npcs: []}

{:noreply, _state} = Room.handle_cast({:emote, {:user, user}, message}, state)

assert_received {:"$gen_cast", {:notify, {"room/heard", %Message{message: "emote"}}}}
end

test "updating player data" do
state = %{players: [%User{id: 11, name: "Player"}], npcs: []}

Copy path View file
@@ -24,7 +24,8 @@ defmodule Web.RoomTest do

state = Game.Zone._get_state(zone.id)
children = state.room_supervisor_pid |> Supervisor.which_children()
assert children |> length() == 1
# room + event bus
assert children |> length() == 2

assert Game.Room._get_state(room.id)
end
@@ -37,7 +38,8 @@ defmodule Web.RoomTest do
# Check the supervision tree to make sure casts have gone through
state = Game.Zone._get_state(zone.id)
children = state.room_supervisor_pid |> Supervisor.which_children()
assert children |> length() == 1
# room + event bus
assert children |> length() == 2

state = Game.Room._get_state(room.id)
assert state.room.name == "Pathway"
@@ -53,7 +55,8 @@ defmodule Web.RoomTest do
# Check the supervision tree to make sure casts have gone through
state = Game.Zone._get_state(zone.id)
children = state.room_supervisor_pid |> Supervisor.which_children()
assert children |> length() == 1
# room + event bus
assert children |> length() == 2

{:ok, room} = Room.add_item(room, item.id)

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.