・目次
サーバ編
- 第0章 Hello! After World!! - 初心者がelixirでオンラインゲーム製作に挑戦してみた(2018/12/21公開)
- 第1章 チャットの実装(2019/1/21公開)
- 第2章 スキル・道具の実装(2019/2/21公開)
- 第3章 セーブ機能の実装(2019/3/27公開)
- 第4章 サーバ間移動の実装(2019/5/18)
- 第5章 パーティ機能の実装(2019/10/14)
- 第6章 ダンジョン機能の実装(2019/10月下旬予定)
- 第7章 ボス機能の実装(2019/11月予定)
- 第8章 ジョブ機能の実装(2019/11月予定)
- 第9章 トレード機能の実装(2019/11月予定)
- 第10章 未定(2019/11月予定)
- 最終章 ゲーム公開!(2019/12/21予定)
クライアント編
開発ツール編
- 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 |
・今回の目標
今回はパーティ機能を実装します。
パーティ機能ではパーティへの加入、退会、パラメータ同期を行えるようにします。
実装対象は下図の点線枠内です。
・実装
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
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
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
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
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
# =========================
# 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
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
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
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の処理を実行します。
実行
- ユーザA(ID:2)でログインします。
- ユーザB(ID:4)でログインします。
- ユーザAがユーザBに対しパーティ加入処理を実行し、ユーザBにパーティ加入先のパーティIDを通知します。
- パーティメンバ間のパラメータ処理を実行します。
- ユーザ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
・まとめ
- パーティ機能を実装し、動作確認をおこないました。
- 次はダンジョン機能の実装を行う予定です。
- 近日中にゲームのデモ動画を公開予定です。
最後まで読んでいただきありがとうございました。