Skip to content

Commit

Permalink
Merge pull request #73 from ImNotAVirus/feature/item-basics
Browse files Browse the repository at this point in the history
Feature/item basics
  • Loading branch information
ImNotAVirus authored Aug 21, 2024
2 parents d79c55e + cfff336 commit 2645716
Show file tree
Hide file tree
Showing 15 changed files with 740 additions and 34 deletions.
25 changes: 14 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,20 @@ jobs:
app: ${{fromJson(needs.directories.outputs.apps)}}
env:
MIX_ENV: test
# services:
# db:
# image: postgres:11
# ports: ['5432:5432']
# env:
# POSTGRES_PASSWORD: postgres
# options: >-
# --health-cmd pg_isready
# --health-interval 10s
# --health-timeout 5s
# --health-retries 5
services:
db:
image: postgres:latest
ports: ['5432:5432']
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_HOST_AUTH_METHOD: 'trust'
POSTGRES_DB: elvengard_test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Set up Elixir
Expand Down
39 changes: 39 additions & 0 deletions apps/elven_data/lib/elven_data/enums/item_enums.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
defmodule ElvenData.Enums.ItemEnums do
@moduledoc """
TODO: Documentation
"""

import SimpleEnum, only: [defenum: 2]

## Enums

defenum :inventory_type,
# equipped: not present in the game, for DB purpose
equipped: -1,
equipment: 0,
main: 1,
etc: 2,
miniland: 3,
specialist: 6,
costume: 7

defenum :slot_type, [
:main_weapon,
:armor,
:hat,
:gloves,
:boots,
:secondary_weapon,
:necklace,
:ring,
:bracelet,
:mask,
:fairy,
:amulet,
:sp,
:costume_suit,
:costume_hat,
:weapon_skin,
:wings
]
end
7 changes: 7 additions & 0 deletions apps/elven_database/config/runtime.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import Config

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
if File.exists?("#{__DIR__}/runtime.#{config_env()}.exs") do
Code.require_file("runtime.#{config_env()}.exs", __DIR__)
end
8 changes: 8 additions & 0 deletions apps/elven_database/config/runtime.test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Config

config :elven_database, ElvenDatabase.Repo,
database: "elvengard_test",
username: "postgres",
password: "postgres",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox
19 changes: 13 additions & 6 deletions apps/elven_database/lib/elven_database/players/character.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ defmodule ElvenDatabase.Players.Character do
import Ecto.Changeset
# import EctoBitfield

require ElvenData.Enums.PlayerEnums
require ElvenData.Enums.PlayerEnums, as: PlayerEnums

alias ElvenData.Enums.PlayerEnums
alias ElvenDatabase.Players.Item

# FIXME: Later improve this typespec
@type t :: %__MODULE__{}

# defbitfield GameOptions,
# exchange_blocked: round(:math.pow(2, 1)),
Expand All @@ -26,6 +29,8 @@ defmodule ElvenDatabase.Players.Character do
# hats_hidden: round(:math.pow(2, 16)),
# ui_locked: round(:math.pow(2, 17))

## Schema

schema "characters" do
belongs_to :account, ElvenDatabase.Players.Account
field :name, :string
Expand Down Expand Up @@ -80,6 +85,8 @@ defmodule ElvenDatabase.Players.Character do

# field :game_options, GameOptions

has_many :items, Item, foreign_key: :owner_id

timestamps()
end

Expand Down Expand Up @@ -136,8 +143,8 @@ defmodule ElvenDatabase.Players.Character do
@name_regex ~r/^[\x21-\x7E\xA1-\xAC\xAE-\xFF\x{4e00}-\x{9fa5}\x{0E01}-\x{0E3A}\x{0E3F}-\x{0E5B}\x2E]{4,14}$/u

@doc false
def changeset(account, attrs) do
account
def changeset(character, attrs) do
character
|> cast(attrs, @fields)
|> cast_assoc(:account)
|> validate_required(@required_fields)
Expand All @@ -147,8 +154,8 @@ defmodule ElvenDatabase.Players.Character do
end

@doc false
def disabled_changeset(account, attrs) do
account
def disabled_changeset(character, attrs) do
character
|> cast(attrs, @fields)
|> cast_assoc(:account)
|> validate_required(@required_fields)
Expand Down
70 changes: 70 additions & 0 deletions apps/elven_database/lib/elven_database/players/item.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
defmodule ElvenDatabase.Players.Item do
@moduledoc false

use Ecto.Schema

import Ecto.Changeset

require ElvenData.Enums.ItemEnums, as: ItemEnums

alias __MODULE__
alias ElvenDatabase.Players.Character

@type id :: non_neg_integer()
@type t :: %Item{
id: id(),
owner_id: non_neg_integer(),
inventory_type: ItemEnums.inventory_type_keys(),
slot: ItemEnums.slot_type() | non_neg_integer(),
vnum: non_neg_integer(),
quantity: non_neg_integer(),
# Ecto fields
__meta__: Ecto.Schema.Metadata.t(),
inserted_at: any(),
updated_at: any()
}

## Schema

schema "items" do
belongs_to :owner, ElvenDatabase.Players.Character

field :inventory_type, Ecto.Enum, values: ItemEnums.inventory_type(:__keys__)
field :slot, :integer
field :vnum, :integer
field :quantity, :integer

timestamps()
end

## Public API

@fields [
:owner_id,
:inventory_type,
:slot,
:vnum,
:quantity
]

@spec changeset(Item.t(), map()) :: Ecto.Changeset.t()
def changeset(%Item{} = item, attrs) do
attrs =
case attrs do
%{slot: slot} when is_atom(slot) -> Map.put(attrs, :slot, ItemEnums.slot_type(slot))
attrs -> attrs
end

attrs =
case attrs do
%{owner: %Character{} = owner} -> Map.put(attrs, :owner_id, owner.id)
attrs -> attrs
end

item
|> cast(attrs, @fields)
|> foreign_key_constraint(:owner_id)
|> validate_required(@fields)
|> unique_constraint(:slot, name: :owner_inventory_slot)
end
end
76 changes: 76 additions & 0 deletions apps/elven_database/lib/elven_database/players/items.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
defmodule ElvenDatabase.Players.Items do
@moduledoc """
TODO: Documentation
"""

import Ecto.Query, only: [from: 2]

alias ElvenDatabase.Players.Item
alias ElvenDatabase.Repo

# Dyalizer doesn't like `Item.changeset(%Item{}, attrs)`
# because fields on Item struct can't be nil
@dialyzer [
{:no_return, create: 1, create!: 1},
{:no_fail_call, create: 1, create!: 1}
]

## Public API

@spec create(map()) :: {:ok, Item.t()} | {:error, Ecto.Changeset.t()}
def create(attrs) do
%Item{}
|> Item.changeset(attrs)
|> Repo.insert()
end

@spec create!(map()) :: Item.t()
def create!(attrs) do
%Item{}
|> Item.changeset(attrs)
|> Repo.insert!()
end

@spec get(Item.id()) :: {:ok, Item.t()} | {:error, :not_found}
def get(id) do
case Repo.get(Item, id) do
nil -> {:error, :not_found}
item -> {:ok, item}
end
end

@spec get!(Item.id()) :: Item.t()
def get!(id) do
Repo.get!(Item, id)
end

@spec list_by_owner(non_neg_integer()) :: [Item.t()]
def list_by_owner(character_id) do
from(c in Item, where: c.owner_id == ^character_id)
|> Repo.all()
end

@spec update(Item.t(), map()) :: {:ok, Item.t()} | {:error, Ecto.Changeset.t()}
def update(%Item{} = item, attrs) do
item
|> Item.changeset(attrs)
|> Repo.update()
end

@spec update!(Item.t(), map()) :: {:ok, Item.t()} | {:error, Ecto.Changeset.t()}
def update!(%Item{} = item, attrs) do
item
|> Item.changeset(attrs)
|> Repo.update!()
end

@spec delete(Item.t()) :: {:ok, Item.t()} | {:error, Ecto.Changeset.t()}
def delete(%Item{} = item) do
Repo.delete(item)
end

@spec delete!(Item.t()) :: {:ok, Item.t()} | {:error, Ecto.Changeset.t()}
def delete!(%Item{} = item) do
Repo.delete!(item)
end
end
10 changes: 8 additions & 2 deletions apps/elven_database/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ defmodule ElvenDatabase.MixProject do
elixir: "~> 1.13",
start_permanent: Mix.env() == :prod,
deps: deps(),
aliases: aliases()
aliases: aliases(),
elixirc_paths: elixirc_paths(Mix.env())
]
end

# Specifies which paths to compile per environment.
defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]

# Run "mix help compile.app" to learn about applications.
def application do
[
Expand All @@ -32,7 +37,8 @@ defmodule ElvenDatabase.MixProject do
defp aliases do
[
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"]
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"]
]
end
end
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
defmodule ElvenDatabase.Repo.Migrations.CreateAccounts do
use Ecto.Migration

require ElvenDatabase.EctoEnumHelpers
require ElvenData.Enums.PlayerEnums
require ElvenDatabase.EctoEnumHelpers, as: EctoEnumHelpers
require ElvenData.Enums.PlayerEnums, as: PlayerEnums

alias ElvenDatabase.EctoEnumHelpers
alias ElvenData.Enums.PlayerEnums

def change do
def change() do
execute(
EctoEnumHelpers.create_query(PlayerEnums, :authority),
EctoEnumHelpers.drop_query(:authority)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
defmodule ElvenDatabase.Repo.Migrations.CreateCharacters do
use Ecto.Migration

require ElvenDatabase.EctoEnumHelpers
require ElvenData.Enums.PlayerEnums
require ElvenDatabase.EctoEnumHelpers, as: EctoEnumHelpers
require ElvenData.Enums.PlayerEnums, as: PlayerEnums

alias ElvenDatabase.EctoEnumHelpers
alias ElvenData.Enums.PlayerEnums

def change do
def change() do
execute(
EctoEnumHelpers.create_query(PlayerEnums, :character_class),
EctoEnumHelpers.drop_query(:character_class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule ElvenDatabase.Repo.Migrations.CreateItems do
use Ecto.Migration

require ElvenDatabase.EctoEnumHelpers, as: EctoEnumHelpers
require ElvenData.Enums.ItemEnums, as: ItemEnums

def change() do
execute(
EctoEnumHelpers.create_query(ItemEnums, :inventory_type),
EctoEnumHelpers.drop_query(:inventory_type)
)

create table(:items) do
add :owner_id, references(:characters), null: false
add :inventory_type, :inventory_type_enum, null: false
add :slot, :int2, null: false
add :vnum, :int2, null: false
add :quantity, :int2, null: false

timestamps()
end

create unique_index(:items, [:owner_id, :inventory_type, :slot], name: :owner_inventory_slot)
end
end
Loading

0 comments on commit 2645716

Please sign in to comment.