今天记录一下自己构建出来的,使用GenServer调控多个线程,用于实现4人组队的相关逻辑代码.
defmodule PartyAttackController1 do
use GenServer
def start_link(), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
def get_target(uuid) do
GenServer.call PartyAttackController1, {:get_target, uuid}
end
def set_target({uuid, serial}) do
GenServer.cast PartyAttackController1, {:set_target, {uuid, serial}}
end
def leave_member(name) do
GenServer.cast PartyAttackController1, {:leave_member, name}
end
def get_all_member_enter_state(uuid) do
GenServer.call PartyAttackController1, {:get_all_member_enter_state, uuid}
end
def set_dragon_member({uuid, server_id}) do
GenServer.cast PartyAttackController1, {:dragon_member_register, {uuid, server_id}}
end
def get_member_identity({uuid, server_id}) do
GenServer.call PartyAttackController1, {:get_member_identity, {uuid, server_id}}
end
def relieve_acc({uuid, server_id, identity}) do
GenServer.cast PartyAttackController1, {:relieve_acc, {uuid, server_id, identity}}
end
def get_invite_members({uuid, server_id}) do
GenServer.call PartyAttackController1, {:get_invite_members, {uuid, server_id}}
end
def check_state() do
GenServer.call PartyAttackController1, :check_state
end
def allot(members, acc) when length(members) < 4 do
Map.put acc, :members, members
end
def allot(members = [uuid, uuid1, uuid2, uuid3 | tail], acc) do
master_members = (acc[:master_members] || []) ++ [uuid]
member_members = (acc[:member_members] || []) ++ [uuid1, uuid2, uuid3]
allot_members = (acc[:allot_members] || %{}) |> Map.merge(%{uuid => %{members: [uuid1, uuid2, uuid3]}})
sum_members = (acc[:sum_members] || %{}) |> Map.merge(%{uuid => %{master: uuid}, uuid1 => %{master: uuid}, uuid2 => %{master: uuid}, uuid3 => %{master: uuid}})
acc = %{
master_members: master_members,
member_members: member_members,
allot_members: allot_members,
sum_members: sum_members
}
allot(tail, acc)
end
def init(state) do
{:ok, %{}}
end
def handle_call(:check_state, _from, state) do
{:reply, state, state}
end
def handle_call({:get_invite_members, {uuid, server_id}}, _from, state) do
members = state[:fight_members][:allot][uuid][:members]
{:reply, members, state}
end
def handle_cast({:relieve_acc, {uuid, server_id, identity}}, state) do
local_master_members = state[:master_members][server_id] || []
local_member_members = state[:member_members][server_id] || []
fight_members = state[:fight_members] || %{}
state = case identity do
:master ->
local_master_members = local_master_members -- [uuid]
state = put_in(state, [:master_members, server_id], local_master_members)
mem_uuids = (fight_members[:allot][uuid][:members] || [])
fight_members = put_in(fight_members, [:sum], Map.drop(fight_members[:sum], ([uuid] ++ mem_uuids))) |> put_in([:allot], Map.delete(fight_members[:allot], uuid))
local_member_members = local_member_members -- mem_uuids
state = put_in(state, [:member_members, server_id], local_member_members)
state = put_in(state, [:fight_members], fight_members)
:member ->
local_member_members = local_member_members -- [uuid]
state = put_in(state, [:member_members, server_id], local_member_members)
fight_members = put_in(fight_members, [:sum], Map.drop(fight_members[:sum], [uuid]))
state = put_in(state, [:fight_members], fight_members)
_ ->
state
end
{:noreply, state}
end
def handle_call({:get_member_identity, {uuid, server_id}}, _from, state) do
local_master_members = state[:master_members][server_id] || []
local_member_members = state[:member_members][server_id] || []
{identity, state} = cond do
!state[:fight_members][:sum][uuid] ->
{:unknow, state}
uuid in local_master_members ->
mem = state[:fight_members][:sum][uuid] |> Map.merge(%{fight: true})
state = put_in(state, [:fight_members, :sum, uuid], mem)
{:master, state}
uuid in local_member_members ->
mem = state[:fight_members][:sum][uuid] |> Map.merge(%{fight: true})
state = put_in(state, [:fight_members, :sum, uuid], mem)
{:member, state}
true ->
{:unknow, state}
end
{:reply, identity, state}
end
def handle_cast({:dragon_member_register, {uuid, server_id}}, state) do
members = state[:members] || %{}
doing_fight_members = state[:fight_members][:sum] || %{}
allot_members = state[:fight_members][:allot] || %{}
sum_members = state[:fight_members][:sum] || %{}
local_members = members[server_id] || []
local_master_members = state[:master_members][server_id] || []
local_member_members = state[:member_members][server_id] || []
# already allot
flag = uuid in local_members || uuid in Map.keys(doing_fight_members) || uuid in local_master_members || uuid in local_member_members
state = cond do
flag ->
state
true ->
local_members = :lists.append(local_members, [uuid])
acc = %{
master_members: local_master_members,
member_members: local_member_members,
allot_members: allot_members,
sum_members: sum_members
}
acc = allot(local_members, acc)
local_master_members = acc[:master_members] || []
local_member_members = acc[:member_members] || []
local_members = acc[:members] || []
allot_members = acc[:allot_members] || %{}
sum_members = acc[:sum_members] || %{}
members = put_in(members, [server_id], local_members)
master_members = put_in((state[:master_members] || %{}), [server_id], local_master_members)
member_members = put_in((state[:member_members] || %{}), [server_id], local_member_members)
fight_members = put_in((state[:fight_members] || %{}), [:allot], allot_members)
fight_members = put_in(fight_members, [:sum], sum_members)
state = Map.merge(state, %{master_members: master_members, member_members: member_members, members: members, fight_members: fight_members})
end
{:noreply, state}
end
def handle_call({:get_target,uuid}, _from, state) do
master = state[:fight_members][:sum][uuid][:master]
target = state[:fight_members][:allot][master][:target]
{:reply, target, state}
end
def handle_cast({:set_target, {uuid,serial}}, state) do
master = state[:fight_members][:sum][uuid][:master]
mas = state[:fight_members][:allot][master] |> Map.merge(%{target: serial})
state = put_in(state, [:fight_members, :allot, master], mas)
{:noreply, state}
end
def handle_cast({:leave_member, name}, state) do
enter_members = (state[:enter_members] || []) |> List.delete(name)
state = Map.merge state, %{enter_members: enter_members}
{:noreply, state}
end
end
整个代码搭建的数据结构是下面这样
# %{
# members: %{server_id: [member1, member2, member3 ...]},
# master_members: %{server_id: [master_member1, master_member2 ...]},
# member_members: %{server_id: [member_member1, member_member2 ...]},
# fight_members: %{
# allot: %{uuid => %{members: [uuid1, ...], target: serial}},
# sum: %{uuid_mem => %{fight: true, master: uuid_mas}}
# }
# }
主要的组队逻辑就是当线程执行的时候,调用register_把成员的uuid注册到GenServer中的state,根据人物的server_id,把成员uuid添加到相应server_id下,
每当此server_id中的成员数大于等于4的时候都会进行成员身份分配,
排在第一位的成员是队长,进而根据server_id被存储到master_members中, 而后面的3个uuid都根据server_id被相继储存到member_members中,(用于往后的get_identity获取成员身份做准备)
确定的一组成员以队长的uuid作为key值把数据储存到fight_member中的allot,(用于往后的队长邀请队员做准备)
而每个成员都会以
%{
mem_uuid => %{master: mas_uuid}
}
的数据形式储存在fight_members中的sum中(用于往后确定成员应该共同攻击的monster),相应的逻辑是每个过来找怪物id的进程都是先找到自己的队长,然后在allot -> server_id -> mas_uuid -> target_id
紧接着下面就是复杂的解除账号的逻辑,如果是master账号,就从allot中找到其成员uuid,删除其allot中的数据,也把fight_members中的每个成员的数据以及master_members和member_members中的相关成员的数据都删除,
如果是队员的uuid,就只是把fight_members和member_members中相关成员数据删除.
什么!你说allot中还留有残余的队员的垃圾数据?好吧,这个就是此算法逻辑的bug吧,不过队员和队长是同时离开副本的所以基本上都是队长在干清理数据的活,