defmodule Kalevala.Communication do
@moduledoc """
Handle communication for the game
Register global channels, room channels, character channels, etc.
"""
require Logger
alias Kalevala.Communication.Cache
alias Kalevala.Communication.Channel
alias Kalevala.Event
alias Kalevala.Event.Message
defmacro __using__(_opts) do
quote do
use Supervisor
alias Kalevala.Communication.Cache
alias Kalevala.Communication.Channel
alias Kalevala.Communication.Channels
@behaviour Kalevala.Communication
def start_link(opts, genserver_opts \\ []) do
Supervisor.start_link(__MODULE__, opts, genserver_opts)
end
@doc false
def config(config_overrides) do
default_config = [
channels_name: __MODULE__.Channels,
cache_name: __MODULE__.Cache,
channel_ets_key: __MODULE__.Channels,
subscriber_ets_key: __MODULE__.Subscribers
]
Keyword.merge(default_config, config_overrides)
end
@impl true
def init(config_overrides) do
config = config(config_overrides)
children = [
{Channels, [name: config[:channels_name], config: config]},
{Cache, [name: config[:cache_name], config: [{:channels, initial_channels()} | config]]}
]
Supervisor.init(children, strategy: :one_for_one)
end
@impl true
def initial_channels(), do: []
def register(channel_name, callback_module, options, config_overrides \\ []) do
cache_name = config(config_overrides)[:cache_name]
Kalevala.Communication.register(cache_name, channel_name, callback_module, options)
end
@doc """
Subscribe the current pid to the channel
"""
def subscribe(channel_name, options, config_overrides \\ []) do
channel_ets_key = config(config_overrides)[:channel_ets_key]
Kalevala.Communication.subscribe(channel_ets_key, channel_name, self(), options)
end
@doc """
Unsubscribe the current pid to the channel
"""
def unsubscribe(channel_name, options, config_overrides \\ []) do
channel_ets_key = config(config_overrides)[:channel_ets_key]
Kalevala.Communication.unsubscribe(channel_ets_key, channel_name, self(), options)
end
def subscribers(channel_name, config_overrides \\ []) do
subscriber_ets_key = config(config_overrides)[:subscriber_ets_key]
Cache.subscribers(subscriber_ets_key, channel_name)
end
def publish(channel_name, event, options, config_overrides \\ []) do
channel_ets_key = config(config_overrides)[:channel_ets_key]
Kalevala.Communication.publish(channel_ets_key, channel_name, event, options)
end
defoverridable initial_channels: 0
end
end
@callback initial_channels() :: []
@doc """
Register a new channel with a callback
"""
def register(pid, channel_name, callback_module, options) do
Cache.register(pid, channel_name, callback_module, options)
end
@doc """
Subscribe the current PID to a channel
"""
def subscribe(channel_ets_key, channel_name, subscriber_pid, options) do
case :ets.lookup(channel_ets_key, channel_name) do
[{^channel_name, pid}] ->
Channel.subscribe(pid, channel_name, subscriber_pid, options)
_ ->
:error
end
end
@doc """
Unsubscribe the current PID to a channel
"""
def unsubscribe(channel_ets_key, channel_name, subscriber_pid, options) do
case :ets.lookup(channel_ets_key, channel_name) do
[{^channel_name, pid}] ->
Channel.unsubscribe(pid, channel_name, subscriber_pid, options)
_ ->
:error
end
end
@doc """
Publish a message on a channel
"""
def publish(channel_ets_key, channel_name, event = %Event{topic: Message}, options) do
case :ets.lookup(channel_ets_key, channel_name) do
[{^channel_name, pid}] ->
Channel.publish(pid, event, options)
_ ->
{:error, :channel_not_found}
end
end
def publish(_channel_ets_key, channel_name, message, _options) do
Logger.warn("""
Trying to publish #{inspect(message)} on `#{channel_name}`.
Only events with a topic of #{__MODULE__} allowed.
""")
:error
end
end