summaryrefslogtreecommitdiff |
diff options
Diffstat (limited to 'src/shared')
-rw-r--r-- | src/shared/include/db_item.hrl | 15 | ||||
-rw-r--r-- | src/shared/include/db_query.hrl | 63 | ||||
-rw-r--r-- | src/shared/include/db_user.hrl | 7 | ||||
-rw-r--r-- | src/shared/io/sh_database.erl | 137 | ||||
-rw-r--r-- | src/shared/io/sh_timed_cache.erl | 130 | ||||
-rw-r--r-- | src/shared/io/sh_timed_caches_manager.erl | 152 | ||||
-rw-r--r-- | src/shared/struct/sh_attributes.erl | 108 | ||||
-rw-r--r-- | src/shared/struct/sh_db_item.erl | 70 | ||||
-rw-r--r-- | src/shared/struct/sh_db_query.erl | 59 | ||||
-rw-r--r-- | src/shared/struct/sh_db_user.erl | 28 | ||||
-rw-r--r-- | src/shared/struct/sh_statistics.erl | 193 |
11 files changed, 962 insertions, 0 deletions
diff --git a/src/shared/include/db_item.hrl b/src/shared/include/db_item.hrl new file mode 100644 index 0000000..86d5863 --- /dev/null +++ b/src/shared/include/db_item.hrl @@ -0,0 +1,15 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-record +( + db_item, + { + id :: any(), + perm :: db_user:permission(), + val :: any() + } +). + +-type db_item() :: #db_item{}. + diff --git a/src/shared/include/db_query.hrl b/src/shared/include/db_query.hrl new file mode 100644 index 0000000..7e5a5b0 --- /dev/null +++ b/src/shared/include/db_query.hrl @@ -0,0 +1,63 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-record +( + set_field, + { + field :: non_neg_integer(), + value :: any() + } +). + +-record +( + add_to_field, + { + field :: non_neg_integer(), + values :: list(any()), + head :: boolean() + } +). + +-record +( + update_indexed, + { + field :: non_neg_integer(), + ix :: non_neg_integer(), + ops :: list(db_query_op()) + } +). + +-record +( + set_perm, + { + perm :: db_user:permission() + } +). + +-record +( + set_val, + { + val :: any() + } +). + +-record +( + db_query, + { + db :: atom(), + id :: any(), + user :: db_user:user(), + ops :: list(db_query_master_op()) + } +). + +-type db_query_op() :: (#set_field{} | #add_to_field{} | #update_indexed{}). +-type db_query_master_op() :: (db_query_op() | #set_perm{} | #set_val{}). +-type db_query() :: #db_query{}. + diff --git a/src/shared/include/db_user.hrl b/src/shared/include/db_user.hrl new file mode 100644 index 0000000..c5b033e --- /dev/null +++ b/src/shared/include/db_user.hrl @@ -0,0 +1,7 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-type db_named_user() :: {'user', any()}. +-type db_user() :: (db_named_user() | 'admin' | 'any'). +-type db_permission() :: (list(db_named_user()) | 'any'). + diff --git a/src/shared/io/sh_database.erl b/src/shared/io/sh_database.erl new file mode 100644 index 0000000..a65b784 --- /dev/null +++ b/src/shared/io/sh_database.erl @@ -0,0 +1,137 @@ +-module(sh_database). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export +( + [ + generate_db/0, + fetch/2, + commit/1 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get_db_node () -> + list_to_atom("db_node@" ++ net_adm:localhost()). + +-spec generate_random_characters + ( + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + non_neg_integer(), + bm_battlemap:type(), + list(bm_location:type()), + list(bm_character:type()) + ) + -> list(bm_character:type()). +generate_random_characters +( + 0, + 0, + _CharactersPerPlayer, + _TotalCharacterCount, + _Battlemap, + _ForbiddenLocations, + Result +) -> + Result; +generate_random_characters +( + MaxPlayerID, + 0, + CharactersPerPlayer, + TotalCharacterCount, + Battlemap, + ForbiddenLocations, + Result +) -> + generate_random_characters + ( + (MaxPlayerID - 1), + CharactersPerPlayer, + CharactersPerPlayer, + TotalCharacterCount, + Battlemap, + ForbiddenLocations, + Result + ); +generate_random_characters +( + MaxPlayerID, + PlayerCharacterCount, + CharactersPerPlayer, + TotalCharacterCount, + Battlemap, + ForbiddenLocations, + Result +) -> + NewCharacter = + bm_character:random + ( + TotalCharacterCount, + list_to_binary(integer_to_list(MaxPlayerID)), + bm_battlemap:get_width(Battlemap), + bm_battlemap:get_height(Battlemap), + ForbiddenLocations + ), + Character = + case MaxPlayerID of + 0 -> bm_character:set_is_active(true, NewCharacter); + _ -> NewCharacter + end, + + generate_random_characters + ( + MaxPlayerID, + (PlayerCharacterCount - 1), + CharactersPerPlayer, + (TotalCharacterCount + 1), + Battlemap, + [bm_character:get_location(Character)|ForbiddenLocations], + [Character|Result] + ). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec generate_db () -> 'ok'. +generate_db () -> + BattlemapWidth = sh_roll:between(16, 64), + BattlemapHeight = sh_roll:between(16, 64), + Battlemap = bm_battlemap:random(0, BattlemapWidth, BattlemapHeight), + Characters = generate_random_characters(1, 8, 8, 0, Battlemap, [], []), + PlayersAsList = [bm_player:new(<<"0">>), bm_player:new(<<"1">>)], + Battle = bm_battle:new(<<"0">>, PlayersAsList, Battlemap, Characters), + + {atomic, ok} = + rpc:call + ( + get_db_node(), + storage_access, + insert, + [battle_db, <<"0">>, any, Battle] + ), + + ok. + +-spec fetch (atom(), any()) -> ({'ok', any()} | 'not_found'). +fetch (DB, ObjectID) -> + {atomic, Reply} = + rpc:call(get_db_node(), storage_access, read, [DB, ObjectID]), + io:format("~ndb_shim:fetch(~p) -> ~p.~n", [{DB, ObjectID}, Reply]), + Reply. + +-spec commit (db_query:type()) -> 'ok'. +commit (Query) -> + {atomic, ok} = rpc:call(get_db_node(), storage_access, query, [Query]), + io:format("~ndb_shim:commit(~p) -> ok.~n", [Query]), + ok. diff --git a/src/shared/io/sh_timed_cache.erl b/src/shared/io/sh_timed_cache.erl new file mode 100644 index 0000000..1839992 --- /dev/null +++ b/src/shared/io/sh_timed_cache.erl @@ -0,0 +1,130 @@ +-module(sh_timed_cache). +-behavior(gen_server). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% 'gen_server' Exports +-export +( + [ + init/1, + handle_cast/2, + handle_call/3, %% No reply will ever be given. + terminate/2, + code_change/3, + format_status/2, + handle_info/2 + ] +). + +%%%% Actual Interface +-export +( + [ + fetch/3, + update/4, + invalidate/3 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec add_to_cache (atom(), any(), any()) -> any(). +add_to_cache (DB, Owner, ObjectID) -> + {ok, TimerPID} = sh_gen_server:start(?MODULE, {DB, {Owner, ObjectID}}, []), + {ok, Data} = sh_database:fetch(DB, ObjectID), + ets:insert(DB, {{Owner, ObjectID}, TimerPID, Data}), + Data. + +-spec add_update_to_cache (atom(), any(), any(), any()) -> 'ok'. +add_update_to_cache (DB, Owner, ObjectID, Data) -> + {ok, TimerPID} = gen_server:start(?MODULE, {DB, {Owner, ObjectID}}, []), + ets:insert(DB, {{Owner, ObjectID}, TimerPID, Data}), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% 'gen_server' functions +init ({DB, ObjectID}) -> + io:format("~nCache entry added: ~p.~n", [{DB, ObjectID}]), + {ok, {DB, ObjectID}, sh_timed_caches_manager:get_timeout()}. + +handle_call (invalidate, _, State) -> + {stop, normal, State}; +handle_call (ping, _, State) -> + {noreply, State, sh_timed_caches_manager:get_timeout()}. + +handle_cast (invalidate, State) -> + {stop, normal, State}; +handle_cast (ping, State) -> + {noreply, State, sh_timed_caches_manager:get_timeout()}. + +terminate (_, {DB, ObjectID}) -> + io:format + ( + "~nCache entry timed out or was invalidated: ~p.~n", + [{DB, ObjectID}] + ), + ets:delete(DB, ObjectID). + +code_change (_, State, _) -> + {ok, State}. + +format_status (_, [_, State]) -> + [{data, [{"State", State}]}]. + +handle_info(timeout, State) -> + {stop, normal, State}; +handle_info(_, {DB, ObjectID}) -> + {noreply, {DB, ObjectID}, timed_caches_manager:get_timeout()}. + +%%%% Interface Functions +-spec fetch (atom(), any(), any()) -> any(). +fetch (DB, Owner, ObjectID) -> + io:format("~nfetch from cache: ~p.~n", [{DB, {Owner, ObjectID}}]), + case ets:lookup(DB, {Owner, ObjectID}) of + [] -> add_to_cache(DB, Owner, ObjectID); + + [{_, TimerPID, Data}] -> + gen_server:cast(TimerPID, ping), + Data + end. + +-spec update (atom(), any(), any(), any()) -> 'ok'. +update (DB, Owner, ObjectID, Data) -> + io:format("~nUpdating cache: ~p.~n", [{DB, {Owner, ObjectID}}]), + case ets:lookup(DB, {Owner, ObjectID}) of + [] -> ok; + + [{_OwnerID, TimerPID, _Data}] -> + gen_server:stop(TimerPID) + end, + add_update_to_cache(DB, Owner, ObjectID, Data). + +-spec invalidate (atom(), any(), any()) -> 'ok'. +invalidate (DB, Owner, ObjectID) -> + case ets:lookup(DB, {Owner, ObjectID}) of + [] -> + io:format + ( + "~nInvalidation request on non-stored entry: ~p.~n", + [{DB, Owner, ObjectID}] + ), + ok; + + [{_, TimerPID, _}] -> + io:format + ( + "~nInvalidation request on stored entry: ~p.~n", + [{DB, Owner, ObjectID}] + ), + gen_server:stop(TimerPID), + ok + end. diff --git a/src/shared/io/sh_timed_caches_manager.erl b/src/shared/io/sh_timed_caches_manager.erl new file mode 100644 index 0000000..7921552 --- /dev/null +++ b/src/shared/io/sh_timed_caches_manager.erl @@ -0,0 +1,152 @@ +-module(sh_timed_caches_manager). +-behavior(gen_server). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% 'gen_server' Exports +-export( + [ + init/1, + handle_cast/2, + handle_call/3, + terminate/2, + code_change/3, + format_status/2, + handle_info/2 + ] +). + +%%%% Actual Interface +-export( + [ + start/0, + new_cache/3, + delete_cache/2, + get_timeout/0 + ] +) +. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +remove_cache (DB) -> + ets:delete(DB). + +add_cache (DB, none) -> + io:format("~nTimed Caches Manager added a new cache. ~n"), + ets:new( + DB, + [ + set, + public, + named_table, + {keypos, 1}, + {read_concurrency, true}, + {heir, none} + ] + ); +add_cache (DB, Heir) -> + io:format("~nTimed Caches Manager added a new cache. ~n"), + ets:new( + DB, + [ + set, + public, + named_table, + {keypos, 1}, + {read_concurrency, true}, + {heir, Heir, DB} + ] + ). + +inherit_cache (CacheList, DB, Heir) -> + case lists:member(DB, CacheList) of + true -> + ets:setopts(DB, {heir, Heir, DB}), + CacheList; + + false -> + [DB|CacheList] + end. + +remove_cache (CacheList, DB) -> + case lists:member(DB, CacheList) of + true -> + remove_cache(DB), + lists:delete(DB, CacheList); + false -> + CacheList + end. + +add_cache (CacheList, DB, Heir) -> + case lists:member(DB, CacheList) of + true when (Heir =:= none) -> + CacheList; + + true -> + ets:setopts(DB, {heir, Heir, DB}), + CacheList; + + false -> + add_cache(DB, Heir), + [DB|CacheList] + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% 'gen_server' functions +init (CacheList) -> + io:format("~nStarting Timed Caches Manager..."), + {ok, CacheList}. + +handle_call ({remove, CacheName}, _Caller, State) -> + {noreply, remove_cache(State, CacheName)}; +handle_call ({add, CacheName, Heir}, _Caller, State)-> + {noreply, add_cache(State, CacheName, Heir)}; +handle_call ({inherit, CacheName, Heir}, _Caller, State)-> + {noreply, inherit_cache(State, CacheName, Heir)}; +handle_call (terminate, _, State) -> + {stop, normal, State}. + +handle_cast ({remove, CacheName}, State) -> + {noreply, remove_cache(State, CacheName)}; +handle_cast ({add, CacheName, Heir}, State)-> + {noreply, add_cache(State, CacheName, Heir)}; +handle_cast ({inherit, CacheName, Heir}, State)-> + {noreply, inherit_cache(State, CacheName, Heir)}; +handle_cast (terminate, State) -> + {stop, normal, State}. + +terminate (_Reason, []) -> + ok; +terminate (Reason, [CacheName|OtherCaches]) -> + remove_cache(CacheName), + terminate(Reason, OtherCaches). + +code_change (_, State, _) -> + {ok, State}. + +format_status (_, [_, State]) -> + [{data, [{"State", State}]}]. + +handle_info(_, State) -> + {noreply, State}. + +%%%% Interface Functions +start () -> + gen_server:start(timed_caches_manager, [], []). + +new_cache (ManagerPid, DB, Heir) -> + gen_server:cast(ManagerPid, {add, DB, Heir}). + +delete_cache (ManagerPid, DB) -> + gen_server:cast(ManagerPid, {remove, DB}). + +get_timeout () -> + 120000. % 2min. diff --git a/src/shared/struct/sh_attributes.erl b/src/shared/struct/sh_attributes.erl new file mode 100644 index 0000000..93dcfc6 --- /dev/null +++ b/src/shared/struct/sh_attributes.erl @@ -0,0 +1,108 @@ +-module(sh_attributes). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-record +( + attributes, + { + constitution :: integer(), + dexterity :: integer(), + intelligence :: integer(), + mind :: integer(), + speed :: integer(), + strength :: integer() + } +). + +-opaque type() :: #attributes{}. + +-export_type([type/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-export +( + [ + get_constitution/1, + get_dexterity/1, + get_intelligence/1, + get_mind/1, + get_speed/1, + get_strength/1, + + set_constitution/2, + set_dexterity/2, + set_intelligence/2, + set_mind/2, + set_speed/2, + set_strength/2 + ] +). + +%%%% Accessors +-export +( + [ + random/0 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-spec get_constitution (type()) -> integer(). +get_constitution (Att) -> Att#attributes.constitution. + +-spec get_dexterity (type()) -> integer(). +get_dexterity (Att) -> Att#attributes.dexterity. + +-spec get_intelligence (type()) -> integer(). +get_intelligence (Att) -> Att#attributes.intelligence. + +-spec get_mind (type()) -> integer(). +get_mind (Att) -> Att#attributes.mind. + +-spec get_speed (type()) -> integer(). +get_speed (Att) -> Att#attributes.speed. + +-spec get_strength (type()) -> integer(). +get_strength (Att) -> Att#attributes.strength. + +-spec set_constitution (integer(), type()) -> type(). +set_constitution (Val, Att) -> Att#attributes{ constitution = Val }. + +-spec set_dexterity (integer(), type()) -> type(). +set_dexterity (Val, Att) -> Att#attributes{ dexterity = Val }. + +-spec set_intelligence (integer(), type()) -> type(). +set_intelligence (Val, Att) -> Att#attributes{ intelligence = Val }. + +-spec set_mind (integer(), type()) -> type(). +set_mind (Val, Att) -> Att#attributes{ mind = Val }. + +-spec set_speed (integer(), type()) -> type(). +set_speed (Val, Att) -> Att#attributes{ speed = Val }. + +-spec set_strength (integer(), type()) -> type(). +set_strength (Val, Att) -> Att#attributes{ strength = Val }. + +-spec random () -> type(). +random () -> + #attributes + { + constitution = sh_roll:percentage(), + dexterity = sh_roll:percentage(), + intelligence = sh_roll:percentage(), + mind = sh_roll:percentage(), + speed = sh_roll:percentage(), + strength = sh_roll:percentage() + }. diff --git a/src/shared/struct/sh_db_item.erl b/src/shared/struct/sh_db_item.erl new file mode 100644 index 0000000..e499634 --- /dev/null +++ b/src/shared/struct/sh_db_item.erl @@ -0,0 +1,70 @@ +-module(sh_db_item). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-include("../include/db_item.hrl"). + +-type type() :: db_item(). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export_type([type/0]). + +-export +( + [ + new/3, + + get_id/1, + get_permission/1, + get_value/1, + + set_permission/2, + set_value/2, + + get_id_field/0, + get_record_info/0, + get_record_name/0 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec new (any(), db_user:permission(), any()) -> type(). +new (ID, Permission, Value) -> + #db_item + { + id = ID, + perm = Permission, + val = Value + }. + +-spec get_id (type()) -> any(). +get_id (#db_item { id = Result }) -> Result. + +-spec get_permission (type()) -> db_user:permission(). +get_permission (#db_item { perm = Result }) -> Result. + +-spec get_value (type()) -> any(). +get_value (#db_item { val = Result }) -> Result. + +-spec set_permission (db_user:permission(), type()) -> type(). +set_permission (Perm, Item) -> Item#db_item{ perm = Perm }. + +-spec set_value (any(), type()) -> type(). +set_value (Value, Item) -> Item#db_item{ val = Value }. + +-spec get_id_field () -> non_neg_integer(). +get_id_field () -> #db_item.id. + +get_record_info () -> record_info(fields, db_item). + +get_record_name () -> db_item. + diff --git a/src/shared/struct/sh_db_query.erl b/src/shared/struct/sh_db_query.erl new file mode 100644 index 0000000..998568e --- /dev/null +++ b/src/shared/struct/sh_db_query.erl @@ -0,0 +1,59 @@ +-module(sh_db_query). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-include("../include/db_query.hrl"). + +-opaque op() :: db_query_op(). +-opaque type() :: db_query(). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export_type([type/0, op/0]). + +-export +( + [ + new/4, + set_field/2, + add_to_field/2, + update_indexed/3 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec new (atom(), any(), sh_db_user:user(), list(op())) -> type(). +new (DBName, ObjectID, User, Ops) -> + #db_query + { + db = DBName, + id = ObjectID, + user = User, + ops = Ops + }. + +-spec set_field (non_neg_integer(), any()) -> op(). +set_field (Field, Value) -> + #set_field { field = Field, value = Value }. + +-spec add_to_field (non_neg_integer(), list(any())) -> op(). +add_to_field (Field, Values) -> + #add_to_field { field = Field, values = Values }. + +-spec update_indexed + ( + non_neg_integer(), + non_neg_integer(), + list(op()) + ) + -> op(). +update_indexed (Field, IX, Updates) -> + #update_indexed { field = Field, ix = IX, ops = Updates}. diff --git a/src/shared/struct/sh_db_user.erl b/src/shared/struct/sh_db_user.erl new file mode 100644 index 0000000..40a46d3 --- /dev/null +++ b/src/shared/struct/sh_db_user.erl @@ -0,0 +1,28 @@ +-module(sh_db_user). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-include("../include/db_user.hrl"). + +-type user() :: db_user(). +-type permission() :: db_permission(). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-export_type([user/0, permission/0]). + +-export([can_access/2]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec can_access (permission(), user()) -> boolean(). +can_access (_, admin) -> true; +can_access (any, _) -> true; +can_access (List, {'user', User}) -> + lists:member(User, List). diff --git a/src/shared/struct/sh_statistics.erl b/src/shared/struct/sh_statistics.erl new file mode 100644 index 0000000..7cf2b06 --- /dev/null +++ b/src/shared/struct/sh_statistics.erl @@ -0,0 +1,193 @@ +-module(sh_statistics). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% TYPES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-record +( + statistics, + { + movement_points :: non_neg_integer(), + health :: non_neg_integer(), + dodges :: non_neg_integer(), + parries :: non_neg_integer(), + damage_min :: non_neg_integer(), + damage_max :: non_neg_integer(), + accuracy :: non_neg_integer(), + double_hits :: non_neg_integer(), + critical_hits :: non_neg_integer() + } +). + +-opaque type() :: #statistics{}. + +-export_type([type/0]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-export +( + [ + get_movement_points/1, + get_health/1, + get_dodges/1, + get_parries/1, + get_damage_min/1, + get_damage_max/1, + get_accuracy/1, + get_double_hits/1, + get_critical_hits/1, + + get_damages/1 + ] +). + +-export +( + [ + new/2 + ] +). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% LOCAL FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec ceil (float()) -> integer(). +ceil (F) -> + I = trunc(F), + case (F > I) of + true -> (I + 1); + _ -> I + end. + +-spec float_to_int (float()) -> integer(). +float_to_int (F) -> ceil(F). + +-spec min_max (number(), number(), number()) -> number(). +min_max (Min, Max, V) -> min(Max, max(Min, V)). + +-spec average (list(number())) -> number(). +%average ([]) -> 0; +average (L) -> lists:sum(L) / length(L). + +% V | 010 | 030 | 050 | 070 | 100 | +% F | 004 | 023 | 058 | 104 | 200 | +-spec gentle_squared_growth (number()) -> non_neg_integer(). +gentle_squared_growth (V) -> float_to_int(math:pow(V, 1.8) / 20). + +% V | 010 | 030 | 050 | 070 | 100 | +% F | 001 | 005 | 018 | 041 | 100 | +-spec sudden_squared_growth (number()) -> non_neg_integer(). +sudden_squared_growth (V) -> float_to_int(math:pow(V, 2.5) / 1000). + +% V | 010 | 030 | 050 | 070 | 100 | +% F | 002 | 006 | 016 | 049 | 256 | +-spec sudden_exp_growth (number()) -> non_neg_integer(). +sudden_exp_growth (V) -> float_to_int(math:pow(4, V / 25)). + +% V | 010 | 030 | 050 | 070 | 100 | +% F | 040 | 066 | 079 | 088 | 099 | +% Seems too generous, values for attributes below 50 should dip faster and +% lower. +%-spec already_high_slow_growth (non_neg_integer()) -> non_neg_integer(). +%already_high_slow_growth (V) -> float_to_int(30 * math:log((V + 5)/4)). + +-spec damage_base_modifier (non_neg_integer()) -> float(). +damage_base_modifier (Strength) -> ((math:pow(Strength, 1.8) / 2000.0) - 0.75). + +-spec apply_damage_base_modifier + ( + float(), + non_neg_integer() + ) + -> non_neg_integer(). +apply_damage_base_modifier (Modifier, BaseValue) -> + max(0, float_to_int(BaseValue + (BaseValue * Modifier))). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% EXPORTED FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%% Accessors +-spec get_movement_points (type()) -> non_neg_integer(). +get_movement_points (Stats) -> Stats#statistics.movement_points. + +-spec get_health (type()) -> non_neg_integer(). +get_health (Stats) -> Stats#statistics.health. + +-spec get_dodges (type()) -> non_neg_integer(). +get_dodges (Stats) -> Stats#statistics.dodges. + +-spec get_parries (type()) -> non_neg_integer(). +get_parries (Stats) -> Stats#statistics.parries. + +-spec get_damage_min (type()) -> non_neg_integer(). +get_damage_min (Stats) -> Stats#statistics.damage_min. + +-spec get_damage_max (type()) -> non_neg_integer(). +get_damage_max (Stats) -> Stats#statistics.damage_max. + +-spec get_accuracy (type()) -> non_neg_integer(). +get_accuracy (Stats) -> Stats#statistics.accuracy. + +-spec get_double_hits (type()) -> non_neg_integer(). +get_double_hits (Stats) -> Stats#statistics.double_hits. + +-spec get_critical_hits (type()) -> non_neg_integer(). +get_critical_hits (Stats) -> Stats#statistics.critical_hits. + +-spec get_damages (type()) -> {non_neg_integer(), non_neg_integer()}. +get_damages (Stats) -> + { + Stats#statistics.damage_min, + Stats#statistics.damage_max + }. + +-spec new + ( + attributes:type(), + {weapon:id(), weapon:id()} + ) + -> type(). +new (BaseAttributes, WeaponIDs) -> + {ActiveWeaponID, _} = WeaponIDs, + ActiveWeapon = weapon:from_id(ActiveWeaponID), + {MinDamage, MaxDamage} = weapon:get_damages(ActiveWeapon), + Attributes = weapon:apply_to_attributes(BaseAttributes, ActiveWeapon), + Constitution = attributes:get_constitution(Attributes), + Dexterity = attributes:get_dexterity(Attributes), + Intelligence = attributes:get_intelligence(Attributes), + Mind = attributes:get_mind(Attributes), + Speed = attributes:get_speed(Attributes), + Strength = attributes:get_strength(Attributes), + DamageBaseModifier = damage_base_modifier(Strength), + + #statistics + { + movement_points = + gentle_squared_growth + ( + average([Mind, Constitution, Constitution, Speed, Speed, Speed]) + ), + health = + gentle_squared_growth(average([Mind, Constitution, Constitution])), + dodges = + min_max(0, 100, sudden_exp_growth(average([Dexterity, Mind, Speed]))), + parries = + min_max + ( + 0, + 75, + sudden_exp_growth + ( + average([Dexterity, Intelligence, Speed, Strength]) + ) + ), + damage_min = apply_damage_base_modifier(DamageBaseModifier, MinDamage), + damage_max = apply_damage_base_modifier(DamageBaseModifier, MaxDamage), + accuracy = min_max(0, 100, sudden_squared_growth(Dexterity)), + double_hits = + min_max(0, 100, sudden_squared_growth(average([Mind, Speed]))), + critical_hits = min_max(0, 100, sudden_squared_growth(Intelligence)) + }. |