2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

・目次

サーバ編

クライアント編

開発ツール編

  • 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

・実装

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

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

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
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

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

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

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

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

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



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つ起動します。
1つ目(ターミナルA)ではRelaySvr,GmSvr,DbSvrを起動します。
2つ目(ターミナルB)では下表のユーザAの処理を実行します
3つ目(ターミナルC)では下表ユーザBの処理を実行します。

実行

  1. ユーザA(ID:2)でログインします。
  2. ユーザB(ID:4)でログインします。
  3. ユーザAがユーザBに対しパーティ加入処理を実行し、ユーザBにパーティ加入先のパーティIDを通知します。
  4. パーティメンバ間のパラメータ処理を実行します。
  5. ユーザ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

//No5-1の実行結果
//ターミナルB,C
00040002000a0014
00040004000a0014


・まとめ

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

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

2
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?