Hello! After World!! - 初心者がelixirでオンラインゲーム製作に挑戦してみた サーバ編(第5章)



# ・目次
### サーバ編
- [第0章　Hello! After World!! - 初心者がelixirでオンラインゲーム製作に挑戦してみた(2018/12/21公開)](https://qiita.com/GKBR/items/653c8f56f5ac86ab81b0)
- [第1章　チャットの実装(2019/1/21公開)](https://qiita.com/GKBR/items/38610ab5990f58f8967d)
- [第2章　スキル・道具の実装(2019/2/21公開)](https://qiita.com/GKBR/items/64915e1fc5e442a38817)
- [第3章　セーブ機能の実装(2019/3/27公開)](https://qiita.com/GKBR/items/ccbb97b31a52610ac7b2)
- [第4章　サーバ間移動の実装(2019/5/18)](https://qiita.com/GKBR/items/151d5401a609bb55327d)
- 第5章　パーティ機能の実装(2019/10/14)
- 第6章　ダンジョン機能の実装(2019/10月下旬予定)
- 第7章　ボス機能の実装(2019/11月予定)
- 第8章　ジョブ機能の実装(2019/11月予定)
- 第9章　トレード機能の実装(2019/11月予定)
- 第10章　未定(2019/11月予定)
- 最終章　ゲーム公開！(2019/12/21予定)

### クライアント編
- [こちらからどうぞ](https://qiita.com/GKBR/items/37045f7392a2103399c0)

### 開発ツール編
- 2019年公開予定


# ・参考文献 
|No.|書籍名|著者|出版年|
|:--|:--|:--|:--|
|1|MMORPGゲームサーバープログラミング|ナム ジェウク|2005|
|2|オンラインゲームを支える技術　壮大なプレイ空間の舞台裏|中嶋 謙互|2011|
|3|クラウドゲームを作る技術　マルチプレイゲーム開発の新戦力|中嶋 謙互|2018|
|4|ドラゴンクエストXを支える技術　大規模オンラインRPGの舞台裏|青山 公士|2018|
|5|プログラミングElixir|Dave Thomas|2016|

# ・開発環境

|PC|Mac Book Pro (Retina, 13-inch, Late 2013) |
|:--|:--|
|OS|Mojave|
|Memory|8GB|
|言語|elixir v1.7|


# ・今回の目標
今回はパーティ機能を実装します。
パーティ機能ではパーティへの加入、退会、パラメータ同期を行えるようにします。


実装対象は下図の点線枠内です。

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256958/91063b94-2466-bb75-e3fb-3bc18a8b3006.png)





# ・実装

```elixir:lib/relay_svr.ex
defmodule RelaySvr do

  def accept(port) do
    {:ok, listen} = :gen_tcp.listen(port, [:binary, packet: 0, active: false, reuseaddr: true])
    loop_accept(listen)
  end

  defp loop_accept(listen) do

    {:ok, socket} = :gen_tcp.accept(listen)
    IO.puts "client: #{inspect socket}"

    #===========================
    # TCP Process start
    #===========================
    pid = spawn_link(RelaySvr.Svr,:start_link,[socket,[]])
    IO.puts "RlaySvr.Svr: #{inspect pid}"

    loop_accept(listen)
  end

end

defmodule RelaySvr.Svr do

  use GenServer

  def start_link(state,opts) do
    {_,pid} = GenServer.start_link(__MODULE__,state,opts)
    self_pid = self()
    GenServer.cast(:gmsvr1,{:svrin,pid})

    task_pid =spawn_link(RelaySvr.Svr, :serve,[state,pid])
    {status,msg} = :gen_tcp.controlling_process(state, task_pid)
    :ok
  end

  def init(state) do
    self_pid = self()  
    {:ok,state}
  end

  def handle_cast({:svrin},state) do
    IO.puts("RelaySvr.Svr:cast :svrin")
    self_pid = self()
    GenServer.cast(:gmsvr1,{:svrin,{self_pid}})
    {:noreply,state}
  end

  #================================
  # 処理結果をクライアントに返信
  #================================
  def handle_cast({:res,msg},state) do
    :gen_tcp.send(state, msg)
    {:noreply,state}
  end

  #===============================
  # 通知内容をクライアントに返信
  #===============================
  def handle_cast({:notify,msg},state) do
    :gen_tcp.send(state, msg)
    {:noreply,state}
  end

  #===============================
  # リキャストタイムをクライアントに返信
  #===============================
  def handle_info({:res,msg},state) do
    :gen_tcp.send(state, msg)
    {:noreply,state}
  end



  #==============================
  # TCP Server
  #==============================
  def serve(socket,pid) do
    socket
    |> read_line
    |> write_line(socket,pid)

    serve(socket,pid)
  end

  defp read_line(socket) do    
    {:ok, data} = :gen_tcp.recv(socket, 0)   
    data
  end

  defp write_line(line, socket,pid) do
    packet = Relay.Packet.conv(line)
    GenServer.cast(packet.svr_id,{:parse, {pid,packet.data}})
  end

end

defmodule Relay.Packet do
  @enforce_keys [:svr_id, :data]
  defstruct [:svr_id, :data]

  def conv(
    <<
      svr_id::size(16),
      data::binary
    >> = packet
  )do
    case svr_id do
      1-> svr_id = :gmsvr1
      2-> svr_id = :gmsvr2
      3-> svr_id = :gmsvr3
      4-> svr_id = :gmsvr4
      5-> svr_id = :chatsvr
      6-> svr_id = :partysvr
    end
    %Relay.Packet{svr_id: svr_id , data: data}
  end
end

defmodule Relay.RelayPacket do
  @enforce_keys [:svr_id, :data]
  defstruct [:svr_id, :data]

  def new(
    <<
      svr_id::size(16),
      data::binary
    >> = packet
  )do
    %Relay.RelayPacket{svr_id: svr_id, data: data}
  end
end

```

```elixir:lib/gm_svr.ex
defmodule GmSvr do

  use GenServer

  def start_link(init,opts) do
    {_,pid}=GenServer.start_link(__MODULE__, init , opts )

  end

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

  #=============================
  # Svrin
  #=============================
  def handle_cast({:svrin,pid_from},state) do

    Notification.add(:notify,pid_from)
    IO.puts "GmSvr:svrin"


    {:noreply,state}
  end

  #=============================
  # Packet Handling
  #=============================
  def handle_cast({:parse, {pid_from, data}},state) do
      GmSvr.Packet.conv(data)
      |> exec(pid_from)
      #IO.puts("#{inspect packet}")
      #run(pid_from,packet)
      {:noreply,state}
  end

  #=============================
  # Login
  #=============================
  def exec(%GmSvr.LoginPacket{}=packet, pid_from) do
    IO.puts "login id=#{packet.id}"
    Session.start_link(packet.id,pid_from)
    Character.start_link(packet.id,%Character.Data{id: packet.id, hp: 10, mp: 20, atk: 40, def: 30})

  end

  #=============================
  # Move
  #=============================
  def exec(%GmSvr.MvPacket{}=packet, pid_from) do
    IO.puts "mv id=#{packet.id} (x,y)=(#{packet.pos_x},#{packet.pos_y})"
    Notification.notify(:notify,:notify,GmSvr.Packet.to_bin(packet))
    GenServer.cast(:dbsvr1,{:save,{packet.id,packet.pos_x,packet.pos_y,packet.pos_z}})
  end

  #=============================
  # Jump
  #=============================
  def exec(%GmSvr.JumpPacket{}=packet, pid_from) do
    IO.puts "jump id=#{packet.id} (x,y)=(#{packet.pos_x},#{packet.pos_y})"
    Notification.notify(:notify,:notify,GmSvr.Packet.to_bin(packet))
  end

  #=============================
  # Skill
  #=============================
  def exec(%GmSvr.SkillPacket{}=packet, pid_from) do
    IO.puts "skill id=#{packet.id} (skill_id,to_id)=(#{packet.skill_id},#{packet.to_id})"

    #発動者のステータス取得
    data1  = Character.get(packet.id)
    #対象者のステータス取得
    data2  = Character.get(packet.to_id)
    #スキル効果算出
    effect = Skill.calc(packet.skill_id,data1,data2)
    #対象者のステータス更新
    Character.update(packet.to_id,%{data2| hp: data2.hp-effect})

    #リキャストタイム
    Skill.genRecastTime(packet.skill_id)
    |>Enum.map(fn x ->(spawn(GmSvr,:sendRecast,[pid_from,x])) end)

    #スキル効果を周囲に通知
    Notification.notify(:notify,:notify,GmSvr.Packet.to_bin(%GmSvr.CharaPacket{id: data2.id, hp: data2.hp, mp: data2.mp}))

    IO.inspect(Character.get(packet.id))
  end


  #=============================
  # Transfer
  #=============================
  def exec(%GmSvr.TransferPacket{}=packet, pid_from) do
    IO.puts "transfer user_id=#{packet.id} (from_svr_id,to_svr_id)=(#{packet.from_svr_id},#{packet.to_svr_id})"
    pid = self()
    case packet.to_svr_id do
      1-> svr_id = :gmsvr1
      2-> svr_id = :gmsvr2
      3-> svr_id = :gmsvr3
      4-> svr_id = :gmsvr4
    end

    GenServer.cast(svr_id,{:transfer, {pid, %GmSvr.InnerPacket{id: packet.id, from_svr_id: packet.from_svr_id, to_svr_id: packet.to_svr_id, user_id: pid_from} }})
  end

  #=============================
  # サーバ間処理
  #=============================
  def handle_cast({:transfer,{ pid, %GmSvr.InnerPacket{}=packet}},state) do
    IO.puts "GmSvr:svrtransfer"

    #サーバ間認証を記述する
    #認証結果を返却
    GenServer.cast(pid, {:res_transfer,{:ok, packet}})
    {:noreply,state}
  end

  def handle_cast({:res_transfer,{ stat ,%GmSvr.InnerPacket{}=packet}},state) do
    IO.puts "GmSvr:svrtransfer ok"

    #移動先サーバをユーザに送信
    p = GmSvr.Packet.to_bin(%GmSvr.TransferPacket{id: packet.id, from_svr_id: packet.from_svr_id, to_svr_id: packet.to_svr_id})
    send(packet.user_id, {:res, p})
    {:noreply,state}
  end


  def sendRecast(pid,time) do
    receive do
    after time ->send(pid,{:res,"send_after"})
    end
  end
end

# =========================
# Packet LOGIN
# =========================
defmodule GmSvr.LoginPacket do
  @enforce_keys [:id]
  defstruct [:id]

  def new(
        <<
          id::unsigned-integer-size(16)
        >> = packet
      ) do
    %GmSvr.LoginPacket{id: id}
  end

end
# =========================
# Packet MOVE
# =========================
defmodule GmSvr.MvPacket do
  @enforce_keys [:id, :pos_x, :pos_y, :pos_z]
  defstruct [:id, :pos_x, :pos_y, :pos_z]

  def new(
        <<
          id::unsigned-integer-size(16),
          pos_x::unsigned-integer-size(16),
          pos_y::unsigned-integer-size(16),
          pos_z::unsigned-integer-size(16)
        >> = packet
      ) do
    %GmSvr.MvPacket{id: id, pos_x: pos_x, pos_y: pos_y, pos_z: pos_z}
  end

end

# =========================
# Packet JUMP
# =========================
defmodule GmSvr.JumpPacket do
  @enforce_keys [:id, :pos_x, :pos_y, :pos_z]
  defstruct [:id, :pos_x, :pos_y, :pos_z]

  def new(
        <<
          id::unsigned-integer-size(16),
          pos_x::unsigned-integer-size(16),
          pos_y::unsigned-integer-size(16),
          pos_z::unsigned-integer-size(16)
        >> = packet
      ) do
    %GmSvr.JumpPacket{id: id, pos_x: pos_x, pos_y: pos_y, pos_z: pos_z}
  end
end

# =========================
# Packet  Chara
#=========================
defmodule GmSvr.CharaPacket do
  @enforce_keys [:id, :hp, :mp]
  defstruct [:id, :hp, :mp]
  def new(
    <<
      id::unsigned-integer-size(16),
      hp::unsigned-integer-size(16),
      mp::unsigned-integer-size(16)
    >> = packet
  )do
    %GmSvr.CharaPacket{id: id, hp: hp, mp: mp}
  end
end

# =========================
# Packet  Transfer
#=========================
defmodule GmSvr.TransferPacket do
  @enforce_keys [:id, :from_svr_id,:to_svr_id]
  defstruct [:id, :from_svr_id, :to_svr_id]
  def new(
    <<
      id::unsigned-integer-size(16),
      from_svr_id::unsigned-integer-size(16),
      to_svr_id::unsigned-integer-size(16)
    >> = packet
  )do
    %GmSvr.TransferPacket{id: id, from_svr_id: from_svr_id, to_svr_id: to_svr_id}
  end
end

# =========================
# Packet  InnerPacket
#=========================
defmodule GmSvr.InnerPacket do
  @enforce_keys [:id, :from_svr_id, :to_svr_id, :user_id]
  defstruct [:id, :from_svr_id, :to_svr_id, :user_id]
  def new(
    <<
      id::unsigned-integer-size(16),
      from_svr_id::unsigned-integer-size(16),
      to_svr_id::unsigned-integer-size(16),
      user_id::unsigned-integer-size(16)
    >> = packet
  )do
    %GmSvr.InnerPacket{id: id, from_svr_id: from_svr_id, to_svr_id: to_svr_id, user_id: user_id}
  end
end

defmodule GmSvr.Packet do
  def conv(
        <<
          pk_type::unsigned-integer-size(16),
          temp::binary
        >> = packet
      ) do
    case pk_type do
      0 -> p = GmSvr.LoginPacket.new(temp)
      1 -> p = GmSvr.MvPacket.new(temp)
      2 -> p = GmSvr.JumpPacket.new(temp)
      3 -> p = GmSvr.SkillPacket.new(temp)
      4 -> p = GmSvr.CharaPacket.new(temp)
      5 -> p = GmSvr.TransferPacket.new(temp)
      6 -> p = GmSvr.ResponsePacket.new(temp)
    end
  end

  def to_bin(%GmSvr.LoginPacket{}=packet)do
    <<
        0::unsigned-integer-size(16)
    >>
  end

  def to_bin(%GmSvr.MvPacket{}=packet)do
    <<
        1::unsigned-integer-size(16),
        packet.id::unsigned-integer-size(16),
        packet.pos_x::unsigned-integer-size(16),
        packet.pos_y::unsigned-integer-size(16),
        packet.pos_z::unsigned-integer-size(16)
    >>
  end

  def to_bin(%GmSvr.JumpPacket{}=packet)do
    <<
        2::unsigned-integer-size(16),
        packet.id::unsigned-integer-size(16),
        packet.pos_x::unsigned-integer-size(16),
        packet.pos_y::unsigned-integer-size(16),
        packet.pos_z::unsigned-integer-size(16)
    >>
  end

  def to_bin(%GmSvr.SkillPacket{}=packet)do
    <<
      3::unsigned-integer-size(16),
      packet.id::unsigned-integer-size(16),
      packet.skill_id::unsigned-integer-size(16),
      packet.to_id::unsigned-integer-size(16)
    >>
  end

  def to_bin(%GmSvr.CharaPacket{}=packet)do
    <<
      4::unsigned-integer-size(16),
      packet.id::unsigned-integer-size(16),
      packet.hp::unsigned-integer-size(16),
      packet.mp::unsigned-integer-size(16)
    >>
  end

  def to_bin(%GmSvr.TransferPacket{}=packet)do
    <<
      5::unsigned-integer-size(16),
      packet.id::unsigned-integer-size(16),
      packet.from_svr_id::unsigned-integer-size(16),
      packet.to_svr_id::unsigned-integer-size(16)
    >>
  end

  def to_bin(%GmSvr.ResponsePacket{}=packet)do
    <<
      6::unsigned-integer-size(16),
      packet.from_svr_id::unsigned-integer-size(16),
      packet.to_svr_id::unsigned-integer-size(16)
    >>
  end
end

```

```elixir:lib/charcter.ex

defmodule Character do
  def start_link(char_id,hp) do
    name = {:via, Registry, {:reg_charid, char_id}}
    {:ok, agent} = Agent.start_link(fn -> hp end , name: name)
    ret = Agent.get(name, &(&1))
    IO.inspect(ret)
  end

  def get(char_id) do
    name = {:via, Registry, {:reg_charid, char_id}}
    Agent.get(name, &(&1))
  end

  def update(char_id, data) do
    name = {:via, Registry, {:reg_charid, char_id}}
    Agent.update(name, fn x -> data end)
  end

  def stop(char_id) do
    name = {:via, Registry, {:reg_charid, char_id}}
    Agent.stop(name)
  end
end

defmodule Character.Data do
  @enforce_keys [:id,:hp,:mp,:atk,:def]
  defstruct [:id,:hp,:mp,:atk,:def]

end
```

```elixir:lib/skill.ex
defmodule Skill do

  def genRecastTime(skillType) do
    case skillType do
      0 -> [1000,2000,3000,4000]
      1 -> [2000,3000,4000,5000]
      2 -> [3000,4000,5000,6000]
      3 -> [4000,5000,6000,7000]
      _ -> [5000,6000,7000,8000]
    end
  end

  def calc(skillType, %Character.Data{}=user_data, %Character.Data{}=target_data) do
    skillEffect = user_data.atk-target_data.def
    cond do
      skillEffect > 0 -> skillEffect
      true -> 0
    end
  end

end

```

```elixir:lib/db_svr.ex
defmodule DbSvr do
  use GenServer

  def start_link(init,opts) do
    start_db()
    {_,pid} = GenServer.start_link(__MODULE__,init,opts)    
  end

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

  def start_db() do
    :mnesia.create_schema([node()])
    :mnesia.start()
    :mnesia.create_table(Chardata,[{:disc_copies, [node()]}, attributes: [:id, :posx, :posy, :posz ]])    
  end

  def handle_cast({:save,{id,posx,posy,posz}},state) do
    :mnesia.transaction(fn() -> :mnesia.write({Chardata,id,posx,posy,posz}) end)
    {:noreply,state}
  end

  def save_db() do
    :mnesia.dump_to_textfile('savedata.txt')
  end

end

```

```elixir
lixir:lib/party_svr.ex
defmodule PartySvr do

  use GenServer

  def start_link(init,opts) do
    {_,pid}=GenServer.start_link(__MODULE__, init , opts )

  end

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

  #=============================
  # Packet Handling
  #=============================
  def handle_cast({:parse, {pid_from, data}},state) do
      PartySvr.Packet.conv(data)
      |> run(pid_from)
      {:noreply,state}
  end

  #=============================
  # Party Reg
  #=============================
  def run(%PartySvr.Reg{}=packet, pid_from) do

    date    = :calendar.universal_time|>Tuple.to_list|>Enum.map(fn(x)-> Tuple.to_list(x) end)|>List.flatten
    partyid = Enum.join([:rand.uniform(1000)| date])|>String.to_integer

    Party.start_link( partyid , [packet.from_id])   
    IO.puts "PartySvr:PartyReg #{partyid}"
    p = PartySvr.Packet.to_bin(%PartySvr.ResReg{id: 10 , party_id: partyid})
    GenServer.cast(pid_from, {:res, p})

  end

  #=============================
  # Party Join
  #=============================
  def run(%PartySvr.PartyJoin{}=packet, pid_from) do
    pid=Session.get(packet.user_id)
    Party.add(packet.party_id , packet.user_id)
    p = PartySvr.Packet.to_bin(%PartySvr.ResJoin{id: 11 , party_id: packet.party_id})
    GenServer.cast(pid, {:res, p})
    IO.puts "PartySvr.PartyJoin"

  end

  #=============================
  # Party Leave
  #=============================
  def run(%PartySvr.PartyLeave{}=packet, pid_from) do
    pid=Session.get(packet.from_id)
    Party.leave(packet.party_id , packet.from_id)
    p = PartySvr.Packet.to_bin(%PartySvr.ResLeave{id: 12 , stat: 100})
    GenServer.cast(pid, {:res, p})
    IO.puts "PartySvr.PartyLeave"

  end

  #=============================
  # Party Sync
  #=============================
  def run(%PartySvr.PartySync{}=packet, pid_from) do    
    Party.get(packet.party_id)
    |>Enum.map(fn id ->
      data   = Character.get(id)
      p      = GmSvr.Packet.to_bin(%GmSvr.CharaPacket{id: data.id, hp: data.hp, mp: data.mp})
      [id,p]
      end)
    |>Enum.map(fn [id,p] ->
      Party.get(packet.party_id)
      |>Enum.map(fn id ->
        pid=Session.get(id)
        GenServer.cast(pid,{:res, p})
       end)
    end)

    IO.puts "PartySvr.PartySync"

  end
end

```

```elixir:lib/partysvr_packet.ex
# =========================
# PartyPacket Reg
# =========================
defmodule PartySvr.Reg do
  @enforce_keys [:from_id]
  defstruct [:from_id]

  def new(
        <<
          from_id::unsigned-integer-size(16),
        >> = packet
      ) do
    %PartySvr.Reg{from_id: from_id}
  end

end

# =========================
# PartyPacket Join
# =========================
  defmodule PartySvr.PartyJoin do
    @enforce_keys [:from_id,:party_id,:user_id]
    defstruct [:from_id,:party_id,:user_id]

    def new(
          <<
            from_id::unsigned-integer-size(16),
            party_id::unsigned-integer-size(64),
            user_id::unsigned-integer-size(16),
          >> = packet
        ) do
      %PartySvr.PartyJoin{from_id: from_id, party_id: party_id, user_id: user_id}
    end
  end

# =========================
# PartyPacket Leave
# =========================
defmodule PartySvr.PartyLeave do
  @enforce_keys [:from_id,:party_id]
  defstruct [:from_id,:party_id]

  def new(
        <<
          from_id::unsigned-integer-size(16),
          party_id::unsigned-integer-size(64)
        >> = packet
      ) do
    %PartySvr.PartyLeave{from_id: from_id, party_id: party_id}
  end
end

# =========================
# PartyPacket Sync
# =========================
defmodule PartySvr.PartySync do
  @enforce_keys [:from_id,:party_id]
  defstruct [:from_id,:party_id]

  def new(
        <<
          from_id::unsigned-integer-size(16),
          party_id::unsigned-integer-size(64)
        >> = packet
      ) do
    %PartySvr.PartySync{from_id: from_id, party_id: party_id}
  end
end


# =========================
# PartyPacket ResReg
# =========================
defmodule PartySvr.ResReg do
  @enforce_keys [:id,:party_id]
  defstruct [:id,:party_id]

  def new(
        <<
        id::unsigned-integer-size(16),
        party_id::unsigned-integer-size(64)
        >> = packet
      ) do
    %PartySvr.ResReg{id: id ,party_id: party_id}
  end

end

# =========================
# PartyPacket ResJoin
# =========================
defmodule PartySvr.ResJoin do
  @enforce_keys [:id,:party_id]
  defstruct [:id,:party_id]

  def new(
        <<
        id::unsigned-integer-size(16),
        party_id::unsigned-integer-size(64)
        >> = packet
      ) do
    %PartySvr.ResJoin{id: id ,party_id: party_id}
  end
end

# =========================
# PartyPacket ResLeave
# =========================
defmodule PartySvr.ResLeave do
  @enforce_keys [:id,:stat]
  defstruct [:id,:stat]

  def new(
        <<
        id::unsigned-integer-size(16),
        stat::unsigned-integer-size(16)
        >> = packet
      ) do
    %PartySvr.ResLeave{id: id ,stat: stat}
  end

end

defmodule PartySvr.Packet do
  def conv(
        <<
          pk_type::unsigned-integer-size(16),
          temp::binary
        >> = packet
      ) do
    case pk_type do
      0  -> p = PartySvr.Reg.new(temp)
      1  -> p = PartySvr.PartyJoin.new(temp)
      2  -> p = PartySvr.PartyLeave.new(temp)
      3  -> p = PartySvr.PartySync.new(temp)
      10 -> p = PartySvr.RespReg.new(temp)
      11 -> p = PartySvr.RespJoin.new(temp)
      12 -> p = PartySvr.RespLeave.new(temp)      
    end
  end

  def to_bin(%PartySvr.Reg{}=packet)do
    <<
        0::unsigned-integer-size(16),
        packet.from_id::unsigned-integer-size(16)
    >>
  end

  def to_bin(%PartySvr.PartyJoin{}=packet)do
    <<
        1::unsigned-integer-size(16),
        packet.from_id::unsigned-integer-size(16),
        packet.party_id::unsigned-integer-size(64),
        packet.user_id::unsigned-integer-size(16)
    >>
  end

  def to_bin(%PartySvr.PartyLeave{}=packet)do
    <<
        2::unsigned-integer-size(16),
        packet.from_id::unsigned-integer-size(16),
        packet.party_id::unsigned-integer-size(64)
    >>
  end

  def to_bin(%PartySvr.PartySync{}=packet)do
    <<
        3::unsigned-integer-size(16),
        packet.from_id::unsigned-integer-size(16),
        packet.party_id::unsigned-integer-size(64)
    >>
  end

  def to_bin(%PartySvr.ResReg{}=packet)do
    <<
        10::unsigned-integer-size(16),
        packet.party_id::unsigned-integer-size(64)
    >>
  end

  def to_bin(%PartySvr.ResJoin{}=packet)do
    <<
        11::unsigned-integer-size(16),
        packet.party_id::unsigned-integer-size(64)
    >>
  end

  def to_bin(%PartySvr.ResLeave{}=packet)do
    <<
        12::unsigned-integer-size(16),
        packet.stat::unsigned-integer-size(16)
    >>
  end

end

```

```elixir:lib/party.ex
defmodule Party do

  def start_link(party_id,hp) do
    name = {:via, Registry, {:reg_party, party_id}}
    {:ok, agent} = Agent.start_link(fn -> hp end , name: name)
    ret = Agent.get(name, &(&1))
    IO.inspect(ret)
  end

  def get(party_id) do
    name = {:via, Registry, {:reg_party, party_id}}
    Agent.get(name, &(&1))
  end

  def update(party_id, pid) do
    name = {:via, Registry, {:reg_party, party_id}}
    Agent.update(name, fn x -> pid end)
  end

  def stop(party_id) do
    name = {:via, Registry, {:reg_party, party_id}}
    Agent.stop(name)
  end

  def add(topic,val) do
    ids = get(topic)
    ids = [val|ids]
    update(topic,ids)
  end

  def leave(topic,val) do
    ids = get(topic)
    ids = List.delete(ids ,val)
    update(topic,ids)
  end

end

```

```elixir:lib/session.ex
defmodule Session do

  def start_link(char_id,hp) do
    name = {:via, Registry, {:reg_session, char_id}}
    {:ok, agent} = Agent.start_link(fn -> hp end , name: name)
    ret = Agent.get(name, &(&1))
    IO.inspect(ret)
  end

  def get(char_id) do
    name = {:via, Registry, {:reg_session, char_id}}
    Agent.get(name, &(&1))
  end

  def update(char_id, pid) do
    name = {:via, Registry, {:reg_session, char_id}}
    Agent.update(name, fn x -> pid end)
  end

  def stop(char_id) do
    name = {:via, Registry, {:reg_session, char_id}}
    Agent.stop(name)
  end
end



```

```elixir:lib/mmo_svr/application.ex
defmodule MmoSvr.Application do 
  @moduledoc false

  use Application

  def start(_type, _args) do    
    import Supervisor.Spec
    children = [
      supervisor(Task.Supervisor, [[name: RelaySvr.TaskSupervisor]]),
     
      %{
        id: :reg_5,
        start: {Registry,:start_link,[ :unique,  :reg_charid, [partitions: System.schedulers_online]]},
        modules: [Registry]
      },
       %{
        id: :reg_6,
        start: {Registry,:start_link,[ :unique,  :reg_party, [partitions: System.schedulers_online]]},
        modules: [Registry]
      },
      %{
        id: :reg_7,
        start: {Registry,:start_link,[ :unique,  :reg_session, [partitions: System.schedulers_online]]},
        modules: [Registry]
      },
      %{
        id: :dbsvr1,
        start: {DbSvr,:start_link,[1,[name: :dbsvr1]]},
        modules: [DbSvr]
      },

      worker(Task,[RelaySvr,:accept,[4040]]),

      %{
        id: :gmsvr_1,
        start: {GmSvr,:start_link,[1,[name: :gmsvr1]]},
        modules: [GmSvr]
      },
      %{
        id: :gmsvr_2,
        start: {GmSvr,:start_link,[2,[name: :gmsvr2]]},
        modules: [GmSvr]
      },
      %{
        id: :gmsvr_3,
        start: {GmSvr,:start_link,[3,[name: :gmsvr3]]},
        modules: [GmSvr]
      },
      %{
        id: :gmsvr_4,
        start: {GmSvr,:start_link,[4,[name: :gmsvr4]]},
        modules: [GmSvr]
      },
      %{
        id: :partysvr,
        start: {PartySvr,:start_link,[6,[name: :partysvr]]},
        modules: [PartySvr]
      }
    ]

    opts = [strategy: :one_for_one, name: MmoSvr.Supervisor]
    Supervisor.start_link(children, opts)
  end
end
```

# ・テスト
パーティ機能のパーティ加入、退会、パラメータ同期処理が正常に動作するか確認します。
 
### 準備
 ターミナルを3つ起動します。
 １つ目(ターミナルA)ではRelaySvr,GmSvr,DbSvrを起動します。
 ２つ目(ターミナルB)では下表のユーザAの処理を実行します
 ３つ目(ターミナルC)では下表ユーザBの処理を実行します。

### 実行
 1. ユーザA(ID:2)でログインします。
 1. ユーザB(ID:4)でログインします。
 1. ユーザAがユーザBに対しパーティ加入処理を実行し、ユーザBにパーティ加入先のパーティIDを通知します。
 1. パーティメンバ間のパラメータ処理を実行します。
 1. ユーザBをパーティから退会させます。


|No.|送信者|送信内容|送信パケット(16進数)|受信者|受信パケット|
|:--|:--|:--|:--|:--|:--|:--|
|1|ユーザA|ログイン|000100000002|-|-|
|2|ユーザB|ログイン|000100000004|-|-|
|3-1|ユーザA|パーティ加入(パーティID取得)|000600000002|ユーザA|000a0000935b02fb0a1c|
|3-2|ユーザA|パーティ加入(パーティ加入)|0006000100020000935b02fb0a1c0004|ユーザB|000100020000935b02fb0a1c0004|
|4-1|Partyサーバ|パラメータ同期|0006000300020000935b02fb0a1c|-|-|
|4-2|-|パラメータ同期|-|ユーザA,B|00040002000a0014|
|4-3|-|パラメータ同期|-|ユーザA,B|00040004000a0014|
|5|ユーザB|パーティ退会|0006000200020000935b02fb0a1c|-|-|


### 実行結果

```
//No.3-1の実行結果
//ターミナルB
000a0000935b02fb0a1c

//No.3-2の実行結果
//ターミナルC
000100020000935b02fb0a1c0004

//No５-１の実行結果
//ターミナルB,C
00040002000a0014
00040004000a0014


```


# ・まとめ
- パーティ機能を実装し、動作確認をおこないました。
- 次はダンジョン機能の実装を行う予定です。
- 近日中にゲームのデモ動画を公開予定です。

最後まで読んでいただきありがとうございました。