Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

923 handle unit selection in backend #1081

Merged
merged 15 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/arena/lib/arena/entities.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ defmodule Arena.Entities do
position: position,
direction: direction,
character_name: character_name,
skin_name: skin_name,
config: config,
now: now,
team: team
Expand Down Expand Up @@ -74,6 +75,7 @@ defmodule Arena.Entities do
last_skill_triggered_inside_bush: now,
natural_healing_damage_interval: character.natural_healing_damage_interval,
character_name: character.name,
skin_name: skin_name,
forced_movement: false,
power_ups: 0,
power_up_damage_modifier: config.game.power_up_damage_modifier,
Expand Down Expand Up @@ -437,6 +439,7 @@ defmodule Arena.Entities do
stamina_interval: get_in(entity, [:aditional_info, :stamina_interval]),
recharging_stamina: get_in(entity, [:aditional_info, :recharging_stamina]),
character_name: get_in(entity, [:aditional_info, :character_name]),
skin_name: get_in(entity, [:aditional_info, :skin_name]),
effects: get_in(entity, [:aditional_info, :effects]),
power_ups: get_in(entity, [:aditional_info, :power_ups]),
inventory: get_in(entity, [:aditional_info, :inventory]),
Expand Down
1 change: 1 addition & 0 deletions apps/arena/lib/arena/game_updater.ex
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,7 @@ defmodule Arena.GameUpdater do
position: pos,
direction: direction,
character_name: player.character_name,
skin_name: player.skin_name,
config: config,
now: now
}
Expand Down
4 changes: 2 additions & 2 deletions apps/arena/lib/arena/matchmaking/deathmatch_mode.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ defmodule Arena.Matchmaking.DeathmatchMode do
@impl true
def init(_) do
Process.send_after(self(), :launch_game?, 300)
Process.send_after(self(), :update_params, 20_000)
Process.send_after(self(), :update_params, 30_000)
{:ok, %{clients: [], batch_start_at: 0}}
end

Expand Down Expand Up @@ -101,7 +101,7 @@ defmodule Arena.Matchmaking.DeathmatchMode do
game_mode_configuration
end

Process.send_after(self(), :update_params, 5000)
Process.send_after(self(), :update_params, 30_000)
{:noreply, Map.put(state, :game_mode_configuration, game_mode_configuration)}
end

Expand Down
4 changes: 2 additions & 2 deletions apps/arena/lib/arena/matchmaking/duo_mode.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule Arena.Matchmaking.DuoMode do
@impl true
def init(_) do
Process.send_after(self(), :launch_game?, 300)
Process.send_after(self(), :update_params, 20_000)
Process.send_after(self(), :update_params, 30_000)
{:ok, %{clients: [], batch_start_at: 0}}
end

Expand Down Expand Up @@ -89,7 +89,7 @@ defmodule Arena.Matchmaking.DuoMode do
game_mode_configuration
end

Process.send_after(self(), :update_params, 5000)
Process.send_after(self(), :update_params, 30_000)
{:noreply, Map.put(state, :game_mode_configuration, game_mode_configuration)}
end

Expand Down
25 changes: 16 additions & 9 deletions apps/arena/lib/arena/matchmaking/game_launcher.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ defmodule Arena.Matchmaking.GameLauncher do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end

def join(client_id, character_name, player_name) do
GenServer.call(__MODULE__, {:join, client_id, character_name, player_name})
def join(params) do
GenServer.call(__MODULE__, {:join, params})
end

def leave(client_id) do
Expand All @@ -23,18 +23,19 @@ defmodule Arena.Matchmaking.GameLauncher do
@impl true
def init(_) do
Process.send_after(self(), :launch_game?, 300)
Process.send_after(self(), :update_params, 20_000)
Process.send_after(self(), :update_params, 30_000)
{:ok, %{clients: [], batch_start_at: 0}}
end

@impl true
def handle_call({:join, client_id, character_name, player_name}, {from_pid, _}, %{clients: clients} = state) do
def handle_call({:join, params}, {from_pid, _}, %{clients: clients} = state) do
batch_start_at = maybe_make_batch_start_at(state.clients, state.batch_start_at)

client = %{
client_id: client_id,
character_name: character_name,
name: player_name,
client_id: params.client_id,
character_name: params.character_name,
skin_name: params.skin_name,
name: params.player_name,
from_pid: from_pid,
type: :human
}
Expand Down Expand Up @@ -95,7 +96,7 @@ defmodule Arena.Matchmaking.GameLauncher do
game_mode_configuration
end

Process.send_after(self(), :update_params, 5000)
Process.send_after(self(), :update_params, 30_000)
{:noreply, Map.put(state, :game_mode_configuration, game_mode_configuration)}
end

Expand Down Expand Up @@ -124,7 +125,13 @@ defmodule Arena.Matchmaking.GameLauncher do
Enum.map(1..missing_clients//1, fn i ->
client_id = UUID.generate()

%{client_id: client_id, character_name: Enum.random(characters).name, name: Enum.at(bot_names, i - 1), type: :bot}
%{
client_id: client_id,
skin_name: "Basic",
character_name: Enum.random(characters).name,
name: Enum.at(bot_names, i - 1),
type: :bot
}
end)
end

Expand Down
4 changes: 2 additions & 2 deletions apps/arena/lib/arena/matchmaking/trio_mode.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ defmodule Arena.Matchmaking.TrioMode do
@impl true
def init(_) do
Process.send_after(self(), :launch_game?, 300)
Process.send_after(self(), :update_params, 20_000)
Process.send_after(self(), :update_params, 30_000)
{:ok, %{clients: [], batch_start_at: 0}}
end

Expand Down Expand Up @@ -89,7 +89,7 @@ defmodule Arena.Matchmaking.TrioMode do
game_mode_configuration
end

Process.send_after(self(), :update_params, 5000)
Process.send_after(self(), :update_params, 30_000)
{:noreply, Map.put(state, :game_mode_configuration, game_mode_configuration)}
end

Expand Down
1 change: 1 addition & 0 deletions apps/arena/lib/arena/serialization/messages.pb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ defmodule Arena.Serialization.Player do
field(:max_health, 20, proto3_optional: true, type: :uint64, json_name: "maxHealth")
field(:inventory, 21, repeated: true, type: Arena.Serialization.Player.InventoryEntry, map: true)
field(:blocked_actions, 22, proto3_optional: true, type: :bool, json_name: "blockedActions")
field(:skin_name, 23, proto3_optional: true, type: :string, json_name: "skinName")
end

defmodule Arena.Serialization.Effect do
Expand Down
17 changes: 15 additions & 2 deletions apps/arena/lib/arena/socket_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,36 @@ defmodule Arena.SocketHandler do
user_id
end

gateway_url = Application.get_env(:arena, :gateway_url)
url = "#{gateway_url}/curse/users/#{user_id}/get_unit"

{:ok, %{character_name: character_name, skin_name: skin_name}} =
case Finch.build(:get, url, [{"content-type", "application/json"}])
|> Finch.request(Arena.Finch) do
{:ok, payload} ->
{:ok, Jason.decode!(payload.body, [{:keys, :atoms}])}

{:error, _} ->
{:error, %{}}
end

matchmaking_queue = Matchmaking.get_queue(:cowboy_req.binding(:mode, req))
character_name = :cowboy_req.binding(:character_name, req)
player_name = :cowboy_req.binding(:player_name, req)

{:cowboy_websocket, req,
%{
client_id: user_id,
matchmaking_queue: matchmaking_queue,
character_name: character_name,
skin_name: skin_name,
player_name: player_name
}}
end

@impl true
def websocket_init(state) do
Logger.info("Websocket INIT called")
state.matchmaking_queue.join(state.client_id, state.character_name, state.player_name)
state.matchmaking_queue.join(state)
joined_msg = LobbyEvent.encode(%LobbyEvent{event: {:joined, %JoinedLobby{}}})
{:reply, {:binary, joined_msg}, state}
end
Expand Down
2 changes: 1 addition & 1 deletion apps/arena/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule Arena.MixProject do
def project do
[
app: :arena,
version: "0.19.1",
version: "0.20.1",
build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,7 @@ defmodule ArenaLoadTest.Serialization.Player do
)

field(:blocked_actions, 22, proto3_optional: true, type: :bool, json_name: "blockedActions")
field(:skin_name, 23, proto3_optional: true, type: :string, json_name: "skinName")
end

defmodule ArenaLoadTest.Serialization.Effect do
Expand Down
1 change: 1 addition & 0 deletions apps/bot_manager/lib/protobuf/messages.pb.ex
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,7 @@ defmodule BotManager.Protobuf.Player do
field(:max_health, 20, proto3_optional: true, type: :uint64, json_name: "maxHealth")
field(:inventory, 21, repeated: true, type: BotManager.Protobuf.Player.InventoryEntry, map: true)
field(:blocked_actions, 22, proto3_optional: true, type: :bool, json_name: "blockedActions")
field(:skin_name, 23, proto3_optional: true, type: :string, json_name: "skinName")
end

defmodule BotManager.Protobuf.Effect do
Expand Down
9 changes: 0 additions & 9 deletions apps/game_backend/lib/game_backend/curse_of_mirra/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,6 @@ defmodule GameBackend.CurseOfMirra.Config do
|> Quests.upsert_quests()
end

def get_characters_config() do
{:ok, characters_config_json} =
Application.app_dir(:game_backend, "priv/characters_config.json")
|> File.read()

Jason.decode!(characters_config_json, [{:keys, :atoms}])
|> Map.get(:characters)
end

def import_stores_config() do
curse_of_mirra_id = Utils.get_game_id(:curse_of_mirra)

Expand Down
23 changes: 20 additions & 3 deletions apps/game_backend/lib/game_backend/curse_of_mirra/users.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ defmodule GameBackend.CurseOfMirra.Users do
@moduledoc """
Module to work with users logic
"""
alias GameBackend.Units.Characters
alias GameBackend.Units
alias GameBackend.Repo
alias GameBackend.Users.User
alias GameBackend.Utils
alias GameBackend.CurseOfMirra.Config
alias GameBackend.Configuration

@doc """
Generates a default map with its associations for a new user
Expand All @@ -24,14 +25,17 @@ defmodule GameBackend.CurseOfMirra.Users do
amount_of_users = Repo.aggregate(User, :count)
username = "User_#{amount_of_users + 1}"
##################################################################
version = Configuration.get_current_version()
active_characters = Characters.get_curse_characters_by_version(version.id) |> Enum.filter(fn c -> c.active end)

units =
Enum.reduce(Config.get_characters_config(), [], fn char_params, acc ->
Enum.reduce(active_characters, [], fn char_params, acc ->
acc ++
[
Units.get_unit_default_values(char_params.name)
Units.get_unit_default_values(char_params)
]
end)
|> mark_random_unit_as_selected()

%{
game_id: Utils.get_game_id(:curse_of_mirra),
Expand All @@ -42,4 +46,17 @@ defmodule GameBackend.CurseOfMirra.Users do
last_daily_quest_generation_at: NaiveDateTime.utc_now()
}
end

defp mark_random_unit_as_selected(units) do
random_index = Enum.random(0..(length(units) - 1))

Enum.with_index(units)
|> Enum.map(fn {unit, index} ->
if index == random_index do
Map.put(unit, :selected, true)
else
unit
end
end)
end
end
59 changes: 53 additions & 6 deletions apps/game_backend/lib/game_backend/units.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ defmodule GameBackend.Units do

import Ecto.Query

alias GameBackend.Utils
alias GameBackend.Units.Characters
alias Ecto.Multi
alias GameBackend.Repo
alias GameBackend.Units.Unit
alias GameBackend.Units.Characters.Character
alias GameBackend.Units.UnitSkin

@doc """
Inserts a unit.
Expand Down Expand Up @@ -105,7 +105,7 @@ defmodule GameBackend.Units do
limit: 1
)
|> Repo.one()
|> Repo.preload([:character, :user])
|> Repo.preload([:character, :user, [skins: :skin]])

@doc """
Gets the user's single selected unit. Fails if they have many or none.
Expand Down Expand Up @@ -134,11 +134,14 @@ defmodule GameBackend.Units do
Fails if there are more than one unit of the same character. Returns nil if there are none.
"""
def get_unit_by_character_name(character_name, user_id) do
character_name = String.downcase(character_name)

case Repo.one(
from(unit in user_units_query(user_id),
join: character in Character,
on: unit.character_id == character.id,
where: character.name == ^character_name
where: character.name == ^character_name,
preload: [skins: :skin]
)
) do
nil -> {:error, :not_found}
Expand Down Expand Up @@ -236,12 +239,56 @@ defmodule GameBackend.Units do
|> Repo.update()
end

def get_unit_default_values(character_name) do
def get_unit_default_values(char_params) do
%{
level: 1,
prestige: 0,
selected: false,
character_id: Characters.get_character_id_by_name_and_game_id(character_name, Utils.get_game_id(:curse_of_mirra))
character_id: char_params.id
}
end

def list_units_by_user(user_id) do
{:ok, Repo.all(from(u in Unit, where: u.user_id == ^user_id, preload: :character))}
end

def select_unit_character(units, character_name) do
character_name = String.downcase(character_name)

Enum.reduce(units, Multi.new(), fn unit, multi ->
Multi.update(
multi,
"select_character_#{unit.id}",
Unit.changeset(unit, %{selected: unit.character.name == character_name})
)
end)
|> Repo.transaction()
end

def select_unit_skin(unit, skin_name) do
Enum.reduce(unit.skins, Multi.new(), fn unit_skin, multi ->
Multi.update(
multi,
"select_skin_#{unit_skin.id}",
UnitSkin.changeset(unit_skin, %{selected: unit_skin.skin.name == skin_name})
)
end)
|> Repo.transaction()
end

def has_skin?(unit, skin_name) do
case Enum.any?(unit.skins, fn unit_skin -> unit_skin.skin.name == skin_name end) do
true -> {:ok, :skin_exists}
_ -> {:error, :skin_not_found}
end
end

@doc """
Inserts a UnitSkin into the database.
"""
def insert_unit_skin(attrs) do
%UnitSkin{}
|> UnitSkin.changeset(attrs)
|> Repo.insert()
end
end
Loading
Loading