View Source Packet Handler

If you remember, in our config.exs, we defined the following lines:

config :login_server, LoginServer.Endpoint.Protocol,
  packet_handler: LoginServer.Endpoint.PacketHandler,
  ...

Once the packets have been decoded by our Network Codec, they will be redirected to this module. This module must implement the ElvenGard.Network.PacketHandler protocol.

A Packet Handler is the module that will manage the logic associated with our client packets.

Let's see how to create one for our demo application.

create-a-packethandler

Create a PacketHandler

# file: lib/login_server/endpoint/packet_handler.ex
defmodule LoginServer.Endpoint.PacketHandler do
  @moduledoc """
  LoginServer.Endpoint.PacketHandler
  """

  @behaviour ElvenGard.Network.PacketHandler

  alias ElvenGard.Network.Socket

  alias LoginServer.ClientPackets.{PingRequest, LoginRequest}
  alias LoginServer.PacketViews

  ## Handlers

  @impl true
  def handle_packet(%PingRequest{}, socket) do
    render = PacketViews.render(:pong_response, %{time: DateTime.utc_now()})
    :ok = Socket.send(socket, render)
    {:cont, socket}
  end

  def handle_packet(%LoginRequest{username: username, password: password}, socket) do
    render =
      if auth_using_db(username, password) do
        PacketViews.render(:login_succeed, %{world: get_worlds_from_manager()})
      else
        PacketViews.render(:login_failed, %{reason: "Bad credentials :/`"})
      end

    :ok = Socket.send(socket, render)
    {:halt, socket}
  end

  ## Fake functions

  defp auth_using_db(username, password) do
    case {username, password} do
      {"admin", "password"} -> true
      _ -> false
    end
  end

  defp get_worlds_from_manager() do
    %{host: "127.0.0.1", port: 5000}
  end
end

Note that each handler takes as parameter a structure with decoded fields representing the packet and the socket associated with it. A handler must return {:cont, new_socket} if we want to continue receiving packets, or {:halt, new_socket} to close the connection to the socket and shutdown the associated GenServer.

Here, our handlers are quite simple: the first will just send to our client a PongResponse packet with the current time and the second will return a LoginSucceed or a LoginFailed depending on the credentials passed in parameter.

Note also that after a PingRequest, we'll continue to handle packets, whereas after a LoginRequest we'll automatically close the connection.

summary

Summary

In this section, we've learned how to handle client packets, create logic around them and use the previously created Views.

Our application is now ready for use. You can view the whole source code at examples/login_server.