Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Havvy committed Oct 2, 2015
1 parent 2afebbe commit 4e0948a
Show file tree
Hide file tree
Showing 14 changed files with 406 additions and 119 deletions.
53 changes: 53 additions & 0 deletions apps/gald/lib/event_queue.ex
@@ -0,0 +1,53 @@
defmodule EventQueue.Handler do
use GenEvent

def init(event_queue) do
{:ok, event_queue}
end

def handle_event(event, event_queue) do
IO.inspect(event)
GenServer.cast(event_queue, {:event, event})
{:ok, event_queue}
end
end

defmodule EventQueue do
@empty_queue :queue.new()

use GenServer

def start_link(event_manager) do
{:ok, self} = GenServer.start_link(__MODULE__, :no_args)
GenEvent.add_handler(event_manager, EventQueue.Handler, self)
{:ok, self}
end

def init(:no_args) do
{:ok, {@empty_queue, nil}}
end

def handle_cast({:event, event}, {queue, nil}) do
{:noreply, {:queue.in(event, queue)}}
end
def handle_cast({:event, event}, {@empty_queue, from}) do
GenServer.reply(from, event)
{:noreply, {@empty_queue, nil}}
end

def handle_cast(:stop, _state) do
{:stop, :normal, nil}
end

def handle_call(:next, from, {@empty_queue, nil}) do
{:noreply, {@empty_queue, from}}
end
def handle_call(:next, _from, {queue, nil}) do
{{:value, event}, queue} = :queue.out(queue)
{:reply, event, queue}
end

def terminate(:normal, _state), do: :ok

def terminate({:function_clause, _error}, _state), do: :ok
end
103 changes: 71 additions & 32 deletions apps/gald/lib/player.ex
@@ -1,50 +1,89 @@
# defmodule Gald.Player do
# use Supervisor

# # Client
# @spec start_link(String.t) :: Supervisor.on_start
# def start_link(name) do
# Supervisor.start_link(__MODULE__, name)
# end

# # Server
# def init(_name) do
# children = []
# supervise(children, strategy: :one_for_all)
# end
# end

defmodule Gald.Player do
defmodule Gald.Players do
use GenServer

# Client
def start_link(name) do
GenServer.start_link(__MODULE__, name)
def start_link(race_out) do
GenServer.start_link(__MODULE__, race_out)
end

def new_player(players, name) do
GenServer.call(players, {:new_player, name})
end

def names(players), do: GenServer.call(players, :names)
def turn_order_deciding_data(players), do: GenServer.call(players, :turndata)

# Server
def init(name) do
{:ok, name}
def init(race_out) do
{:ok, sup} = Supervisor.start_link([Supervisor.Spec.worker(Gald.Player, [])], strategy: :simple_one_for_one)
dict = %{}
join_ix = 0
{:ok, {sup, dict, race_out, join_ix}}
end

def handle_call({:new_player, name}, _from, {sup, dict, race_out, join_ix}) do
if Map.has_key?(dict, name) do
{:error, :duplicate_name}
else
{:ok, player} = Supervisor.start_child(sup, [name])
GenEvent.notify(race_out, {:new_player, name})
dict = Map.put(dict, name, %{pid: player, join_ix: join_ix})
join_ix = join_ix + 1
{:reply, {:ok, player}, {sup, dict, race_out, join_ix}}
end
end

def handle_call(:names, _from, state = {_sup, dict, _race_out, _join_ix}) do
names = dict
|> Enum.sort_by(fn ({_k, %{join_ix: join_ix}}) -> join_ix end)
|> Enum.map(fn ({k, _v}) -> k end)
|> Enum.into([])

{:reply, names, state}
end

def handle_call(:turndata, _from, state = {_sup, dict, _race_out, _join_ix}) do
res = dict
|> Enum.map(fn ({k, %{join_ix: join_ix}}) -> {k, %{join_ix: join_ix}} end)
|> Enum.into(%{})

{:reply, res, state}
end
end

defmodule Gald.Player.Supervisor do
use Supervisor
defmodule Gald.Player do
use GenServer

# Client
@spec start_link() :: Supervisor.on_start
def start_link() do
Supervisor.start_link(__MODULE__, [])
def start_link(name) do
GenServer.start_link(__MODULE__, name)
end

@spec add_player(pid, String.t) :: Supervisor.on_start
def add_player(players, name) do
Supervisor.start_child(players, [name])
def io(player) do
player_in = GenServer.call(player, {:get, :in})
player_out = GenServer.call(player, {:get, :out})
{:ok, player_in, player_out}
end

def name(player), do: GenServer.call(player, {:get, :name})

# Server
def init([]) do
child = [supervisor(Gald.Player, [])]
supervise(child, strategy: :simple_one_for_one)
def init(name) do
# TODO(Havvy): Make this an addressable supervision tree.
{:ok, player_in} = GenServer.start_link(Gald.Player.In, [])
{:ok, player_out} = GenEvent.start_link([])
{:ok, %{
name: name,
in: player_in,
out: player_out
}}
end

def handle_call({:get, component}, _from, state) do
{:reply, state[component], state}
end
end

defmodule Gald.Player.In do
use GenServer
end
94 changes: 60 additions & 34 deletions apps/gald/lib/race.ex
@@ -1,21 +1,23 @@
defmodule Gald.Race do
# TODO(Havvy): Change to a :get_fsm?
use GenServer

@type snapshot :: {Gald.Status.t, any}
@opaque t :: pid

# Client
@spec start_link(%Gald.Config{}) :: {:ok, t}
@spec start_link(%Gald.Config{}) :: {:ok, t, GenEvent.t}
def start_link(config) do
GenServer.start_link(__MODULE__, config)
{:ok, race} = GenServer.start_link(__MODULE__, config)
{:ok, race, race_out(race)}
end

@spec snapshot(t) :: snapshot
def snapshot(race), do: GenServer.call(race, {:snapshot})

def add_player(race, name), do: GenServer.call(race, {:add_player, name})
def new_player(race, name), do: GenServer.call(race, {:new_player, name})

def start_game(race), do: GenServer.cast(race, {:start_game})
def begin(race), do: GenServer.cast(race, {:begin})

@spec move_player(t, any, integer) :: non_neg_integer
def move_player(race, player, space_change) do
Expand All @@ -30,38 +32,45 @@ defmodule Gald.Race do
def is_over(race), do: GenServer.call(race, {:is_over})

@spec config(t) :: %Gald.Config{}
def config(race), do: GenServer.call(race, {:config})
def config(race), do: GenServer.call(race, {:get, :config})

@spec get_name(t) :: String.t
def get_name(race), do: config(race).name

defp race_out(race), do: GenServer.call(race, {:get, :out})

# Server
def init(config) do
import Supervisor.Spec

{:ok, slave} = Supervisor.start_link([], strategy: :one_for_one)
{:ok, status} = Supervisor.start_child(slave, worker(Gald.Status, []))
{:ok, player_sup} = Supervisor.start_child(slave, supervisor(Gald.Player.Supervisor, []))
{:ok, sup} = Supervisor.start_link([], strategy: :one_for_all)
{:ok, out} = Supervisor.start_child(sup, worker(GenEvent, []))
{:ok, players} = Supervisor.start_child(sup, worker(Gald.Players, [out]))

{:ok, %{config: config,
slave: slave,
status: status,
player_sup: player_sup,
players: HashDict.new(),
map: :unstarted}}
sup: sup,
status: :lobby,
out: out,
players: players,
map: :unstarted,
rounds: :unstarted,
turn: :unstarted}}
end

def handle_call({:get, component}, _from, state) do
{:reply, state[component], state}
end

def handle_call({:add_player, name}, _from, state) do
case Gald.Status.get_status(state.status) do
:lobby ->
if Dict.has_key?(state.players, name) do
{:reply, {:error, :duplicate_name}, state}
else
{:ok, player} = Gald.Player.Supervisor.add_player(state.player_sup, name)
{:reply, :ok, Dict.update!(state, :players, &Dict.put(&1, name, player))}
end
_ ->
{:reply, {:error, :already_started}, state}
def handle_call({:new_player, name}, _from, state) do
if state.status == :lobby do
case Gald.Players.new_player(state.players, name) do
{:ok, player} ->
{:reply, Gald.Player.io(player), state}
{:error, reason} ->
{:reply, {:error, reason}, state}
end
else
{:reply, {:error, :already_started}, state}
end
end

Expand All @@ -73,14 +82,14 @@ defmodule Gald.Race do
Gald.Map.move_player(state.map, player, space_change)

if Gald.Map.is_over(state.map) do
Gald.Status.end_game(state.status)
state = Dict.update!(state, :status, fn (:play) -> :over end)
end

{:reply, Gald.Map.get_player_location(state.map, player), state}
end

def handle_call({:snapshot}, _from, state) do
{:reply, Gald.Snapshot.new(Gald.Status.get_status(state.status), state), state}
{:reply, Gald.Snapshot.new(state.status, state), state}
end

def handle_call({:get_player_location, player}, _from, state) do
Expand All @@ -89,21 +98,38 @@ defmodule Gald.Race do

def handle_call({:config}, _from, state), do: {:reply, state.config, state}

def handle_cast({:start_game}, state) do
def handle_cast({:begin}, state) do
import Supervisor.Spec

map_dict = %{players: player_list(state.players),
IO.puts("Beginning the world.")
# Determine Turn Order
rounds_config = %{
players: Gald.Players.turn_order_deciding_data(state.players),
race_out: state.out,
supervisor: state.sup
}
rounds_spec = worker(Gald.Rounds, [rounds_config])
{:ok, rounds} = Supervisor.start_child(state.sup, rounds_spec)

IO.puts("Rounds created.")

# Start the Map
map_dict = %{players: Gald.Players.names(state.players),
end_space: state.config.end_space}
map_spec = worker(Gald.Map, [map_dict])
{:ok, map} = Supervisor.start_child(state.slave, map_spec)
{:ok, map} = Supervisor.start_child(state.sup, map_spec)

Gald.Status.start_game(state.status)
IO.puts("Map created.")

{:noreply, %{state | map: map}}
end
state = %{state | map: map, rounds: rounds, status: :play}
snapshot = Gald.Snapshot.new(state.status, state)
IO.inspect("Sending out snapshot!")
GenEvent.notify(state.out, {:begin, snapshot})

# TODO(Havvy): Have a Player manager thing that can give this for us.
def player_list(players), do: Enum.into(Dict.keys(players), HashSet.new())
Gald.Rounds.begin(rounds)

{:noreply, state}
end

# TODO(Havvy): Have a way to terminate a race.
end
Expand Down
33 changes: 33 additions & 0 deletions apps/gald/lib/rounds.ex
@@ -0,0 +1,33 @@
defmodule Gald.Rounds do
use GenServer

# Client
def start_link(config) do
GenServer.start_link(__MODULE__, config)
end

def begin(rounds), do: GenServer.cast(rounds, :begin)

# Server
def init(%{players: players, race_out: race_out, supervisor: sup}) do
# TODO(Havvy): Should the order be passed to the Players actor?
order = players
|> Enum.sort_by(fn ({_k, %{join_ix: join_ix}}) -> join_ix end)
|> Enum.map(fn ({k, _v}) -> k end)
|> Enum.into([])

{:ok, %{
race_out: race_out,
sup: sup,
order: order,
round: 1,
round_order: order
}}
end

def handle_cast(:begin, state = %{race_out: race_out}) do
GenEvent.notify(race_out, {:round_start, 1})

{:noreply, state}
end
end

0 comments on commit 4e0948a

Please sign in to comment.