├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── apps ├── db │ ├── include │ │ ├── config.hrl │ │ ├── journal.hrl │ │ ├── scoring.hrl │ │ ├── table.hrl │ │ ├── tournaments.hrl │ │ └── transaction.hrl │ ├── rebar.config │ └── src │ │ ├── db.app.src │ │ ├── db_accounts.erl │ │ ├── db_app.erl │ │ ├── db_config.erl │ │ ├── db_journal.erl │ │ ├── db_scoring.erl │ │ ├── db_sup.erl │ │ ├── db_table.erl │ │ └── db_tournaments.erl ├── rebar.config ├── server │ ├── include │ │ ├── authtoken.hrl │ │ ├── basic_types.hrl │ │ ├── game_okey.hrl │ │ ├── game_state.hrl │ │ ├── game_tavla.hrl │ │ ├── journal.hrl │ │ ├── requests.hrl │ │ └── settings.hrl │ ├── rebar.config │ └── src │ │ ├── README.md │ │ ├── auth │ │ ├── anonymous.erl │ │ └── auth_server.erl │ │ ├── game.erl │ │ ├── game_app.erl │ │ ├── game_session.erl │ │ ├── game_sup.erl │ │ ├── lib │ │ ├── deck.erl │ │ ├── gas.erl │ │ ├── known_records.erl │ │ └── midict.erl │ │ ├── modules │ │ ├── elimination.erl │ │ ├── lucky.erl │ │ ├── matrix.erl │ │ ├── relay.erl │ │ └── standalone.erl │ │ ├── okey │ │ ├── okey_bot.erl │ │ ├── okey_desk.erl │ │ ├── okey_reveal.erl │ │ ├── okey_scoring.erl │ │ ├── okey_table.erl │ │ └── okey_test.erl │ │ ├── server.app.src │ │ ├── sup │ │ ├── id_generator.erl │ │ ├── journal.erl │ │ ├── lucky_sup.erl │ │ ├── okey_sup.erl │ │ └── tavla_sup.erl │ │ └── tavla │ │ ├── tavla_bot.erl │ │ ├── tavla_desk.erl │ │ ├── tavla_lib.erl │ │ ├── tavla_paired.erl │ │ ├── tavla_scoring.erl │ │ ├── tavla_table.erl │ │ └── tavla_test.erl └── web │ ├── README.md │ ├── include │ ├── basic_types.hrl │ ├── journal.hrl │ ├── requests.hrl │ ├── settings.hrl │ └── users.hrl │ ├── priv │ ├── static │ │ ├── README.md │ │ ├── app │ │ │ ├── Kakaranet-Scene.svg │ │ │ ├── SVG.txt │ │ │ ├── channel.html │ │ │ ├── css │ │ │ │ └── main.css │ │ │ ├── index.htm │ │ │ ├── js │ │ │ │ ├── bootloader.js │ │ │ │ ├── chat.js │ │ │ │ ├── controller.js │ │ │ │ ├── dragndrop.js │ │ │ │ ├── n2o │ │ │ │ │ ├── bert.js │ │ │ │ │ ├── bullet.js │ │ │ │ │ ├── facebook.js │ │ │ │ │ └── kvs.js │ │ │ │ ├── okey │ │ │ │ │ ├── card.js │ │ │ │ │ ├── deck.js │ │ │ │ │ ├── hand.js │ │ │ │ │ ├── okey.js │ │ │ │ │ └── okey_protocol.js │ │ │ │ ├── player.js │ │ │ │ ├── roster.js │ │ │ │ ├── selector.js │ │ │ │ ├── tavla │ │ │ │ │ └── tavla.js │ │ │ │ ├── timer.js │ │ │ │ └── translations.js │ │ │ ├── mp3 │ │ │ │ └── ding.mp3 │ │ │ └── svg │ │ │ │ ├── Card-Small.svg │ │ │ │ ├── Card.svg │ │ │ │ ├── Deck.svg │ │ │ │ ├── Discarder.svg │ │ │ │ ├── Okey-Rules.svg │ │ │ │ ├── Person-Center-Alina.svg │ │ │ │ ├── Person-Center-Gabrielo.svg │ │ │ │ ├── Person-Center-Mustafa.svg │ │ │ │ ├── Person-Left-Alina.svg │ │ │ │ ├── Person-Left-Gabrielo.svg │ │ │ │ ├── Person-Left-Mustafa.svg │ │ │ │ ├── Person-Right-Alina.svg │ │ │ │ ├── Person-Right-Gabrielo.svg │ │ │ │ ├── Person-Right-Mustafa.svg │ │ │ │ └── Player-Statistics.svg │ │ └── doc │ │ │ ├── Kakaranet-Scene.sketch │ │ │ ├── Data │ │ │ ├── metadata │ │ │ └── version │ │ │ ├── Kakaranet-Scene.svg │ │ │ ├── bert.js │ │ │ ├── bullet.js │ │ │ ├── campaigns_mock.htm │ │ │ ├── contents.htm │ │ │ ├── drag.htm │ │ │ ├── game_server.htm │ │ │ ├── images │ │ │ ├── drag1.png │ │ │ ├── drag2.png │ │ │ ├── drag3.png │ │ │ ├── drag4.png │ │ │ └── drag5.png │ │ │ ├── js_proto.htm │ │ │ ├── kvs.js │ │ │ ├── n2o.js │ │ │ ├── n2o_proto.htm │ │ │ ├── plans │ │ │ └── april2014.htm │ │ │ ├── svg.htm │ │ │ ├── svg.js │ │ │ ├── svg_clean.htm │ │ │ ├── svg_mock.htm │ │ │ ├── synrc.css │ │ │ └── templates │ │ │ ├── ButtonAnimation.svg │ │ │ ├── Card.svg │ │ │ ├── Mustafa-Persona.svg │ │ │ └── Mustafa-Selection.svg │ └── templates │ │ └── index.html │ ├── rebar.config │ └── src │ ├── js_session.erl │ ├── logallow.erl │ ├── okey.erl │ ├── protocol.erl │ ├── routes.erl │ ├── web.app.src │ ├── web_app.erl │ └── web_sup.erl ├── mad ├── orderapps.erl ├── otp.mk ├── rebar.config └── rels └── web ├── files ├── erl ├── install_upgrade.escript ├── node ├── node.cmd ├── nodetool ├── start_erl.cmd ├── sys.config └── vm.args └── reltool.config /.gitignore: -------------------------------------------------------------------------------- 1 | .#* 2 | *~ 3 | apps/face/priv/static/nitrogen 4 | Mnesia* 5 | .applist 6 | erl_crash.dump 7 | */ebin/*.beam 8 | deps 9 | apps/**/ebin/** 10 | rels/web/devbox 11 | index.js 12 | protocol.js 13 | .rebar 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: erlang 2 | otp_release: 3 | - R16B 4 | notifications: 5 | email: 6 | - maxim@synrc.com 7 | install: "make" 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | RELEASE := kakaranet 2 | COOKIE := node_runner 3 | VER := 1.0.0 4 | 5 | default: get-deps compile 6 | 7 | include otp.mk 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | N2O Based Game 2 | ============== 3 | 4 | [![Build Status](https://travis-ci.org/synrc/games.svg?branch=master)](https://travis-ci.org/synrc/games) 5 | 6 | ![Image](http://synrc.com/images/kakaranet.png) 7 | 8 | Run 9 | --- 10 | 11 | To run just perform on Linux, Windows or Mac 12 | 13 | $ make && make console 14 | > kvs:join(). 15 | 16 | And open it in browser [http://localhost:8080/static/app/index.htm](http://localhost:8080/static/app/index.htm) 17 | 18 | Licensing 19 | --------- 20 | 21 | Not allowed for commercial use. 22 | For licensing N2O SVG Example or its parts for commercial use please contact: 23 | 24 | * Maxim Sokhatsky 25 | 26 | OM A HUM 27 | -------------------------------------------------------------------------------- /apps/db/include/config.hrl: -------------------------------------------------------------------------------- 1 | -record(config, {key, value}). 2 | 3 | -define(DBA, kvs:config(dba)). 4 | -------------------------------------------------------------------------------- /apps/db/include/journal.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(JOURNAL_HRL). 2 | -define(JOURNAL_HRL, "journal.hrl"). 3 | 4 | -include_lib("kvs/include/kvs.hrl"). 5 | -define(LOG_HEADER, game_id, date, time, user, module, type, speed, rounds). 6 | -define(CONTAINER_LOG, ?CONTAINER, ?LOG_HEADER, stats=[]). 7 | -record(container_log, {?CONTAINER_LOG}). 8 | -record(container_event, {?ITERATOR(container_log), ?LOG_HEADER }). 9 | 10 | -record(series_log, {?CONTAINER_LOG, score, game_points, kakush }). 11 | -record(series_event, {?ITERATOR(series_log),?LOG_HEADER, result, score}). 12 | 13 | -record(reveal_log, {?CONTAINER_LOG, skill, score }). 14 | -record(reveal_event, {?ITERATOR(reveal_log), ?LOG_HEADER, reason, winner, score, total}). 15 | 16 | -record(protocol_log, {?CONTAINER_LOG}). 17 | -record(protocol_event, {?ITERATOR(protocol_log), ?LOG_HEADER, event, game_event}). 18 | 19 | -endif. 20 | -------------------------------------------------------------------------------- /apps/db/include/scoring.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(SCORING_HRL). 2 | -define(SCORING_HRL, "scoring_hrl"). 3 | 4 | -include_lib("kvs/include/kvs.hrl"). 5 | 6 | -record(ti_game_event, { 7 | id :: integer(), %% GameId 8 | type, 9 | game_name, 10 | game_mode, 11 | double_points, 12 | tournament_type = standalone }). 13 | 14 | -record(pointing_rule, {?ITERATOR(feed), 15 | pointing_rule_id, %% {Game, GameType, Rounds} | {Game, GameType} 16 | game, 17 | game_type, 18 | rounds, %% rounds | points | undefined 19 | kakush_winner, %% kakush points will be assigned for the winner of the game. 20 | kakush_other, %% kakush points will be assigned for the rest of the 21 | quota, 22 | game_points}). 23 | 24 | -endif. 25 | -------------------------------------------------------------------------------- /apps/db/include/table.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(TABLE_HRL). 2 | -define(TABLE_HRL, "table.hrl"). 3 | 4 | -include_lib("kvs/include/kvs.hrl"). 5 | 6 | -record(game_table, {?ITERATOR(feed), 7 | name, 8 | gameid, 9 | trn_id, 10 | game_type, 11 | rounds, 12 | sets, 13 | owner, 14 | timestamp, 15 | users = [], 16 | users_options = [], 17 | game_mode, 18 | game_options, 19 | game_speed, 20 | friends_only, 21 | invited_users = [], 22 | private :: boolean(), 23 | feel_lucky = false :: boolean(), 24 | creator, 25 | age_limit, 26 | groups_only = [], 27 | gender_limit, 28 | location_limit = "", 29 | paid_only, 30 | deny_robots = false :: boolean(), 31 | slang, 32 | deny_observers, 33 | gosterge_finish = false :: boolean(), 34 | double_points = 1 :: integer(), 35 | game_state, 36 | game_process :: pid(), 37 | game_module :: atom(), 38 | pointing_rules :: any(), 39 | pointing_rules_ex :: [], %% [#pointing_rule{}] - list of additional pointing rules, 40 | game_process_monitor :: reference(), 41 | tournament_type = simple :: simple | paired | paired_lobby | tournament, 42 | robots_replacement_allowed = true :: boolean()}). 43 | 44 | -endif. 45 | -------------------------------------------------------------------------------- /apps/db/include/tournaments.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(TOURNAMENTS_HRL). 2 | -define(TOURNAMENTS_HRL, "tournaments.hrl"). 3 | 4 | -include_lib("kvs/include/kvs.hrl"). 5 | 6 | -record(team,{?ITERATOR(feed), 7 | name, %% team name for now will bu just first player username 8 | play_record, %% linked list history of played users under that ticket 9 | type}). 10 | 11 | -record(tournament,{?ITERATOR(feed), 12 | name, %% tournament name 13 | game_type, 14 | description, 15 | creator, 16 | created, 17 | start_date, 18 | start_time, 19 | end_date, 20 | status, %% activated, ongoing, finished, canceled 21 | quota, 22 | tours, 23 | awards, 24 | winners :: list(), %% [{UserId, Position, GiftId}] 25 | waiting_queue, %% play_record, added here when user wants to join tournament 26 | avatar, 27 | owner, 28 | players_count, 29 | speed, 30 | type, 31 | game_mode}). %% eliminatin, pointing, etc 32 | 33 | -record(play_record,{?ITERATOR(feed), 34 | who, %% user 35 | tournament, %% tournament in which user played 36 | team, %% team under which user player tournament 37 | game_id, %% game id that user played under that team 38 | realname, 39 | game_points, 40 | kakush, 41 | kakush_currency, 42 | quota, 43 | other}). 44 | 45 | -endif. 46 | -------------------------------------------------------------------------------- /apps/db/include/transaction.hrl: -------------------------------------------------------------------------------- 1 | 2 | -ifndef(TRANSACTION_HRL). 3 | -define(TRANSACTION_HRL, "transaction_hrl"). 4 | 5 | -include_lib("kvs/include/kvs.hrl"). 6 | 7 | -type currency() :: kakaush | quota | game_point | money. 8 | 9 | -record(transaction, {?ITERATOR(feed), 10 | timestamp :: erlang:now(), 11 | amount :: integer(), 12 | currency :: currency(), 13 | comment, 14 | info}). 15 | 16 | -endif. 17 | -------------------------------------------------------------------------------- /apps/db/rebar.config: -------------------------------------------------------------------------------- 1 | {lib_dirs, ["../../apps"]}. 2 | {deps_dir, ["../../deps"]}. 3 | {deps, [ 4 | {kvs, ".*", {git, "git://github.com/synrc/kvs", {tag,"1.5.0"}}} 5 | ]}. 6 | -------------------------------------------------------------------------------- /apps/db/src/db.app.src: -------------------------------------------------------------------------------- 1 | {application, db, 2 | [ 3 | {description, "KKN Persistent Storage"}, 4 | {vsn, "1"}, 5 | {registered, []}, 6 | {applications, [kernel,stdlib,kvs]}, 7 | {mod, { db_app, []}}, 8 | {env, []} 9 | ]}. 10 | -------------------------------------------------------------------------------- /apps/db/src/db_accounts.erl: -------------------------------------------------------------------------------- 1 | -module(db_accounts). 2 | -include_lib("kvs/include/metainfo.hrl"). 3 | -include_lib("db/include/transaction.hrl"). 4 | -compile(export_all). 5 | 6 | metainfo() -> 7 | #schema{name = kvs, tables = 8 | [ 9 | #table{name = transaction, fields=record_info(fields, transaction)} 10 | ] 11 | }. 12 | -------------------------------------------------------------------------------- /apps/db/src/db_app.erl: -------------------------------------------------------------------------------- 1 | -module(db_app). 2 | -behaviour(application). 3 | -export([start/2, stop/1]). 4 | 5 | start(_StartType, _StartArgs) -> 6 | db_sup:start_link(). 7 | 8 | stop(_State) -> 9 | ok. 10 | -------------------------------------------------------------------------------- /apps/db/src/db_config.erl: -------------------------------------------------------------------------------- 1 | -module(db_config). 2 | -include_lib("kvs/include/metainfo.hrl"). 3 | -include_lib("db/include/config.hrl"). 4 | -compile(export_all). 5 | 6 | metainfo() -> 7 | #schema{name=kvs,tables=[ 8 | #table{name=config,fields=record_info(fields,config)} 9 | ]}. 10 | -------------------------------------------------------------------------------- /apps/db/src/db_journal.erl: -------------------------------------------------------------------------------- 1 | -module(db_journal). 2 | -include_lib("kvs/include/metainfo.hrl"). 3 | -include_lib("db/include/journal.hrl"). 4 | -compile(export_all). 5 | 6 | metainfo() -> 7 | #schema{name=kvs,tables=[ 8 | #table{name=series_log,container=true,fields=record_info(fields,series_log),keys=[?LOG_HEADER]}, 9 | #table{name=reveal_log,container=true,fields=record_info(fields,reveal_log),keys=[?LOG_HEADER]}, 10 | #table{name=protocol_log,container=true,fields=record_info(fields,protocol_log),keys=[?LOG_HEADER]}, 11 | #table{name=series_event,container=series_log,fields=record_info(fields,series_event),keys=[?LOG_HEADER]}, 12 | #table{name=reveal_event,container=reveal_log,fields=record_info(fields,reveal_event),keys=[?LOG_HEADER]}, 13 | #table{name=protocol_event,container=protocol_log,fields=record_info(fields,protocol_event),keys=[?LOG_HEADER]} 14 | ]}. 15 | -------------------------------------------------------------------------------- /apps/db/src/db_scoring.erl: -------------------------------------------------------------------------------- 1 | -module(db_scoring). 2 | -include_lib("kvs/include/metainfo.hrl"). 3 | -include_lib("db/include/scoring.hrl"). 4 | -compile(export_all). 5 | 6 | metainfo() -> 7 | #schema{name = kvs, tables = [ 8 | #table{name = pointing_rule, fields=record_info(fields, pointing_rule)}]}. 9 | 10 | setup() -> 11 | 12 | %% OKEY 13 | 14 | Rounds5 = #pointing_rule{rounds = 5, game = okey, kakush_winner = 15, kakush_other = 3, quota = 5}, 15 | kvs:put(Rounds5#pointing_rule{id = {okey, standard, 5}, game_type = standard, game_points = 1}), 16 | kvs:put(Rounds5#pointing_rule{id = {okey, evenodd, 5}, game_type = evenodd, game_points = 5}), 17 | kvs:put(Rounds5#pointing_rule{id = {okey, color, 5}, game_type = color, game_points = 7}), 18 | 19 | Rounds10 = #pointing_rule{rounds = 10, game = okey, kakush_winner = 15, kakush_other = 3, quota = 10}, 20 | kvs:put(Rounds10#pointing_rule{id = {okey, standard, 10}, game_type = standard, game_points = 7}), 21 | kvs:put(Rounds10#pointing_rule{id = {okey, evenodd, 10}, game_type = evenodd, game_points = 10}), 22 | kvs:put(Rounds10#pointing_rule{id = {okey, color, 10}, game_type = color, game_points = 15}), 23 | 24 | Rounds20 = #pointing_rule{rounds = 20, game = okey, kakush_winner = 15, kakush_other = 3, quota = 20}, 25 | kvs:put(Rounds20#pointing_rule{id = {okey, standard, 20}, game_type = standard, game_points = 15}), 26 | kvs:put(Rounds20#pointing_rule{id = {okey, evenodd, 20}, game_type = evenodd, game_points = 20}), 27 | kvs:put(Rounds20#pointing_rule{id = {okey, color, 20}, game_type = color, game_points = 30}), 28 | 29 | Rounds40 = #pointing_rule{rounds = 40, game = okey, kakush_winner = 32, kakush_other = 7, quota = 40}, 30 | kvs:put(Rounds40#pointing_rule{id = {okey, standard, 40}, game_type = standard, game_points = 30}), 31 | kvs:put(Rounds40#pointing_rule{id = {okey, evenodd, 40}, game_type = evenodd, game_points = 40}), 32 | kvs:put(Rounds40#pointing_rule{id = {okey, color, 40}, game_type = color, game_points = 60}), 33 | 34 | Rounds60 = #pointing_rule{rounds = 60, game = okey, kakush_winner = 32, kakush_other = 7, quota = 40}, 35 | kvs:put(Rounds60#pointing_rule{id = {okey, standard, 60}, game_type = standard, game_points = 60}), 36 | kvs:put(Rounds60#pointing_rule{id = {okey, evenodd, 60}, game_type = evenodd, game_points = 80}), 37 | kvs:put(Rounds60#pointing_rule{id = {okey, color, 60}, game_type = color, game_points = 120}), 38 | 39 | Rounds80 = #pointing_rule{rounds = 80, game = okey, kakush_winner = 70, kakush_other = 15, quota = 80}, 40 | kvs:put(Rounds80#pointing_rule{id = {okey, standard, 80}, game_type = standard, game_points = 120}), 41 | kvs:put(Rounds80#pointing_rule{id = {okey, evenodd, 80}, game_type = evenodd, game_points = 160}), 42 | kvs:put(Rounds80#pointing_rule{id = {okey, color, 80}, game_type = color, game_points = 240}), 43 | 44 | %% Countdown 10 45 | kvs:put(#pointing_rule{id = {okey, countdown}, game = okey, game_type = countdown, 46 | kakush_winner = 6, kakush_other = 1, quota = 8, game_points = 10}), 47 | 48 | %% Feel lucky 49 | kvs:put(#pointing_rule{id = {okey, feellucky}, game = okey, game_type = feellucky, 50 | kakush_winner = 0, kakush_other = 0, quota = 0, game_points = 0}), 51 | 52 | %% TAVLA 53 | 54 | GameTypes = [standard, evenodd], 55 | %% here we store points in rounds field 56 | [kvs:put(#pointing_rule{id = {tavla, GT, 3}, rounds = 3, game = tavla, game_type = GT, 57 | kakush_winner = 1, kakush_other = 1, quota = 3, game_points = 5}) 58 | || GT <- GameTypes], 59 | 60 | 61 | [kvs:put(#pointing_rule{id = {tavla, GT, 5}, rounds = 5, game = tavla, game_type = GT, 62 | kakush_winner = 2, kakush_other = 1, quota = 4, game_points = 5}) 63 | || GT <- GameTypes], 64 | 65 | [kvs:put(#pointing_rule{id = {tavla, GT, 7}, rounds = 7, game = tavla, game_type = GT, 66 | kakush_winner = 3, kakush_other = 1, quota = 5, game_points = 7}) 67 | || GT <- GameTypes], 68 | 69 | %% Kakara 70 | 71 | kvs:put(#pointing_rule{id = {tavla, kakara}, game = tavla, game_type = kakara, 72 | kakush_winner = 0, kakush_other = 0, quota = 1, game_points = 0}). 73 | 74 | -------------------------------------------------------------------------------- /apps/db/src/db_sup.erl: -------------------------------------------------------------------------------- 1 | -module(db_sup). 2 | -behaviour(supervisor). 3 | -export([start_link/0]). 4 | -export([init/1]). 5 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 6 | 7 | start_link() -> 8 | supervisor:start_link({local, ?MODULE}, ?MODULE, []). 9 | 10 | init([]) -> 11 | 12 | RestartStrategy = one_for_one, 13 | MaxRestarts = 1000, 14 | MaxSecondsBetweenRestarts = 3600, 15 | 16 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, 17 | 18 | Restart = permanent, 19 | Shutdown = 2000, 20 | Type = worker, 21 | 22 | {ok, { {one_for_one, 5, 10}, []} }. 23 | 24 | -------------------------------------------------------------------------------- /apps/db/src/db_table.erl: -------------------------------------------------------------------------------- 1 | -module(db_table). 2 | -include_lib("kvs/include/metainfo.hrl"). 3 | -include_lib("db/include/table.hrl"). 4 | -compile(export_all). 5 | 6 | metainfo() -> 7 | #schema{name = kvs, tables = 8 | [ 9 | #table{name = game_table, fields=record_info(fields, game_table)} 10 | ] 11 | }. 12 | -------------------------------------------------------------------------------- /apps/db/src/db_tournaments.erl: -------------------------------------------------------------------------------- 1 | -module(db_tournaments). 2 | -include_lib("kvs/include/metainfo.hrl"). 3 | -include_lib("db/include/tournaments.hrl"). 4 | -compile(export_all). 5 | 6 | metainfo() -> 7 | #schema{name = kvs, tables = 8 | [ 9 | #table{name = team, fields=record_info(fields, team)}, 10 | #table{name = tournament, fields=record_info(fields, tournament)}, 11 | #table{name = play_record, fields=record_info(fields, play_record)} 12 | ] 13 | }. 14 | -------------------------------------------------------------------------------- /apps/rebar.config: -------------------------------------------------------------------------------- 1 | {sub_dirs, ["db","server","web"]}. 2 | {lib_dirs, ["../apps"]}. 3 | {deps_dir, ["../deps"]}. 4 | -------------------------------------------------------------------------------- /apps/server/include/authtoken.hrl: -------------------------------------------------------------------------------- 1 | -record(authtoken, {token :: string(), id}). 2 | 3 | -------------------------------------------------------------------------------- /apps/server/include/basic_types.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(BASIC_TYPES). 2 | -define(BASIC_TYPES, true). 3 | 4 | -type 'PlayerId'() :: any(). 5 | -type 'GameId'() :: any(). 6 | 7 | -record('PlayerInfo', { 8 | id :: 'PlayerId'(), 9 | login :: string(), 10 | name :: string(), 11 | surname :: string(), 12 | seat_num, 13 | connected, 14 | age :: integer(), 15 | skill :: integer(), 16 | score :: integer(), 17 | avatar_url :: string(), 18 | robot = false :: boolean(), 19 | sex }). 20 | 21 | -endif. 22 | -------------------------------------------------------------------------------- /apps/server/include/game_state.hrl: -------------------------------------------------------------------------------- 1 | -define(PLAYER, 2 | id :: pos_integer(), %% Player Id 3 | seat_num :: integer(), 4 | user_id :: binary(), 5 | is_bot :: boolean(), 6 | info :: #'PlayerInfo'{}, 7 | connected :: boolean() 8 | ). 9 | 10 | -define(GAME_STATE, 11 | game_id :: pos_integer(), 12 | table_id :: pos_integer(), 13 | table_name :: string(), 14 | players, %% The register of table players 15 | parent :: {atom(), pid()}, 16 | parent_mon_ref :: reference(), 17 | relay :: pid(), 18 | mult_factor :: integer(), 19 | slang_flag :: boolean(), 20 | observer_flag :: boolean(), 21 | tournament_type :: atom(), %% standalone | elimination | pointing | lucky 22 | speed :: slow | normal | fast, 23 | game_mode :: standard | color | evenodd | countdown, 24 | social_actions_enabled :: boolean(), 25 | tournament_table :: list(), %% [{TurnNum, TurnRes}], TurnRes = [{PlayerId, Points, Status}] 26 | % game tree 27 | rounds :: undefined | integer(), %% Not defined for countdown game type 28 | tour :: undefined | integer(), 29 | tours :: undefined | integer(), 30 | cur_round :: integer(), 31 | next_series_confirmation :: yes_exit | no_exit | no, 32 | % timeouts 33 | turn_timeout :: integer(), 34 | ready_timeout :: integer(), 35 | round_timeout :: infinity | integer(), 36 | set_timeout :: infinity | integer(), 37 | timeout_timer :: undefined | reference(), 38 | timeout_magic :: term(), 39 | round_timer :: undefined | reference(), 40 | set_timer :: undefined | reference(), 41 | wait_list :: list(), 42 | % pause 43 | pause_mode :: disabled | normal, 44 | paused_statename :: atom(), %% For storing a statename before pause 45 | paused_timeout_value :: integer(), %% For storing remain timeout value 46 | % scoring 47 | scoring_state :: term()). 48 | 49 | -record(table_state, {?GAME_STATE}). 50 | 51 | -record(okey_state, {?GAME_STATE, 52 | reveal_confirmation_timeout :: integer(), 53 | reveal_confirmation :: boolean(), 54 | gosterge_finish_allowed :: undefined | boolean(), %% Only defined for countdown game type 55 | %% Dynamic parameters 56 | desk_rule_pid :: undefined | pid(), 57 | start_seat :: integer(), %% The player who moves first 58 | reveal_confirmation_list :: list(), %% {SeatNum, Answer} 59 | desk_state %% OKEY DESK 60 | }). 61 | 62 | -record(tavla_state, {?GAME_STATE, 63 | tables_num :: integer(), %% For paired mode >= 1, otherwise = 1 64 | %% Dynamic parameters 65 | desk_rule_pid :: undefined | pid(), 66 | start_color, %% The player who moves first 67 | desk_state %% TAVLA DESK 68 | }). 69 | -------------------------------------------------------------------------------- /apps/server/include/journal.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(JOURNAL_HRL). 2 | -define(JOURNAL_HRL, "journal.hrl"). 3 | 4 | -include_lib("kvs/include/kvs.hrl"). 5 | -define(LOG_HEADER, game_id, date, time, user, module, type, speed, rounds). 6 | -define(CONTAINER_LOG, ?CONTAINER, ?LOG_HEADER, stats=[]). 7 | -record(container_log, {?CONTAINER_LOG}). 8 | -record(container_event, {?ITERATOR(container_log), ?LOG_HEADER }). 9 | 10 | -record(series_log, {?CONTAINER_LOG, score, game_points, kakush }). 11 | -record(series_event, {?ITERATOR(series_log),?LOG_HEADER, result, score}). 12 | 13 | -record(reveal_log, {?CONTAINER_LOG, skill, score }). 14 | -record(reveal_event, {?ITERATOR(reveal_log), ?LOG_HEADER, reason, winner, score, total}). 15 | 16 | -record(protocol_log, {?CONTAINER_LOG}). 17 | -record(protocol_event, {?ITERATOR(protocol_log), ?LOG_HEADER, event, game_event}). 18 | 19 | -endif. 20 | -------------------------------------------------------------------------------- /apps/server/include/requests.hrl: -------------------------------------------------------------------------------- 1 | -include("basic_types.hrl"). 2 | 3 | % actions are being instantiated from client to server 4 | 5 | -record(session_attach, {token}). 6 | -record(login, {username, password}). 7 | -record(logout, {}). 8 | -record(join_game, {game}). 9 | -record(stats_action, {player_id :: 'PlayerId'(), game_type}). 10 | -record(chat, {game, player_id, who, message }). 11 | -record(game_action, {game :: 'GameId'(), action, args = []}). 12 | -record(pause_game, {table_id :: integer(),game :: 'GameId'(),action}). 13 | 14 | % event notifications from server to client 15 | 16 | -record(game_event, {game :: 'GameId'(), event, args = [] }). 17 | -record(chat_event, {game :: 'GameId'(), player_id :: 'PlayerId'(), who, message }). 18 | -record(stats_event, {player_id :: 'PlayerId'(), games, reveals, protocol, score}). 19 | -record(game_paused, {table_id :: integer(), game :: 'GameId'(),action,who :: 'PlayerId'(),retries}). 20 | -record(disconnect, {reason_id,reason}). 21 | -record(player_left, {player :: 'PlayerId'(),human_replaced=false,replacement::'PlayerId'()}). 22 | -------------------------------------------------------------------------------- /apps/server/include/settings.hrl: -------------------------------------------------------------------------------- 1 | -define(LOBBY_TIMEOUT, begin {ok, X} = kvs:get(config,"games/lobby_timeout", 60000), X end). 2 | -define(REMATCH_TIMEOUT, begin {ok, X} = kvs:get(config,"games/rematch_timeout", 45000), X end). 3 | -define(ROBOT_DELAY, begin {ok, X} = kvs:get(config,"games/okey/robot_delay", 500), X end). 4 | -define(SKILL_SEED, begin {ok, X} = kvs:get(config, "games/okey/skill_seed", 500), X end). %% skill value for new player 5 | -define(IS_TEST, begin {ok, X} = kvs:get(config,"debug/is_test", false), X end). %% determines if code is executed in test 6 | -define(ALL_SESSIONS, <<"all_sessions_topic">>). 7 | -define(TEST_TOKEN, "EBAs6dg2Xw6XuCdg8qiPmlBLgYJ6N4Ti0P+oGpWgYz4NW4nBBUzTe/wAuLYtPnjFpsjCExxSpV78fipmsPxcf+NGy+QKIM6rmVJhpnIlKf0bpFNuGaAPjZAWthhGO8nZ0V8UnA=="). 8 | -define(TEST_TOKEN2, "EBAs6dg2Xw6XuCdg8qiPmlBLgYJ6N4Ti0P+oGpWgYz4NW4nBBUzTe/wAuLYtPnjFpsjCExxSpV78fipmsPxcf+NGy+QKIM6rmVJhpnIlKf0bpFNuGaAPjZAWthhGO8nZ0V8Un2=="). 9 | -define(PAIRED_TAVLA_ASYNC, false). 10 | -define(SOCIAL_ACTION_SUBSCRIBE, 1). 11 | -define(SOCIAL_ACTION_UNSUBSCRIBE, 2). 12 | -define(SOCIAL_ACTION_BLOCK, 3). 13 | -define(SOCIAL_ACTION_UNBLOCK, 4). 14 | -define(SOCIAL_ACTION_LOVE, 5). 15 | -define(SOCIAL_ACTION_HAMMER, 6). 16 | -------------------------------------------------------------------------------- /apps/server/rebar.config: -------------------------------------------------------------------------------- 1 | {lib_dirs, ["../../apps"]}. 2 | {deps_dir, ["../../deps"]}. 3 | {deps,[db]}. 4 | -------------------------------------------------------------------------------- /apps/server/src/README.md: -------------------------------------------------------------------------------- 1 | Kakaranet Game Server 2 | ===================== 3 | 4 | Game Server API 5 | --------------- 6 | 7 | Game Server provides very clean, like a sky, Erlang based API for communication with 8 | underlying transports. The talk protocol is based on two kind of messages: 9 | actions (client requests) and events (server responses). Each client is driven 10 | by game_session gen_server. 11 | 12 | ├── README.md you are reading this file just now 13 | ├── game.erl API for crating game rooms, tournaments, etc. 14 | ├── game_session.erl front API for Game Protocol 15 | ├── game_app.erl 16 | ├── game_sup.erl 17 | ├── server.app.src 18 | 19 | Please refer to requests.hrl for common Game Server protocol envelopment. 20 | Each game could have specific actions and events. 21 | 22 | ### Actions 23 | 24 | ```erlang 25 | -record(session_attach, {token}). 26 | -record(logout, {}). 27 | -record(join_game, {game :: 'GameId'() }). 28 | -record(stats_action, {player :: 'PlayerId'(), game_type}). 29 | -record(pause_game, {table, game :: 'GameId'(), action}). 30 | -record(game_action, {game :: 'GameId'(), action, args = []}). 31 | ``` 32 | 33 | ### Events 34 | 35 | ```erlang 36 | -record(game_event, {game :: 'GameId'(), event, args = [] }). 37 | -record(stats_event, {player :: 'PlayerId'(), games, reveals, protocol}). 38 | -record(game_paused, {table, game :: 'GameId'(), action, who :: 'PlayerId'(), retries}). 39 | -record(disconnect, {code :: integet(), reason}). 40 | -record(player_left, {player :: 'PlayerId'(), human_replaced, replacement :: 'PlayerId'()}). 41 | ``` 42 | 43 | Token Server 44 | ------------ 45 | 46 | Password-free token server assign permanent token to empty cookie or unknown device 47 | automatically. Default unique user name would be assigned also. Later on you can 48 | change your name or merge your account with other devices after subscription. 49 | 50 | ├── auth 51 | │   ├── anonymous.erl anonymous names generation 52 | │   └── auth_server.erl gen_server 53 | 54 | Game Tables 55 | ----------- 56 | 57 | Each game has playing rules (desk), scroring constraints (scoring), 58 | table message protocol (table) and default bot implementation. Please follow 59 | this composition during new games development. Each table represents single game. 60 | 61 | ├── okey/tavla 62 | │   ├── game_bot.erl game bot 63 | │   ├── game_desk.erl desk rules, game rules 64 | │   ├── game_scoring.erl scoring 65 | │   ├── game_table.erl table messaging between players 66 | │   └── game_test.erl game testing 67 | 68 | Tournaments 69 | ----------- 70 | 71 | Games could be organized into a set of tables with tournament rules. By default 72 | Game Server provides several kind of default tournament modules: elimination tounaments, 73 | full cycle tournaments, infinite score-free tournaments for new players and regular 74 | standalone tables (which is treated as simpliest tournaments with just one table). 75 | 76 | ├── modules 77 | │   ├── elimination.erl tournaments 78 | │   ├── matrix.erl declarative tournaments DSL language 79 | │   ├── lucky.erl free play mode, endless tournaments or lobby mode 80 | │   ├── relay.erl relay between tournaments and tables 81 | │   └── standalone.erl regular single-table games 82 | 83 | Services 84 | -------- 85 | 86 | Each tournaments and games are driven by supervisors. Also Game Server has game logging 87 | serices along with auth token sever which are also under supervision. 88 | 89 | ├── sup 90 | │   ├── journal.erl persist game protocol statistics in KV store 91 | │   ├── id_generator.erl unique ids 92 | │   ├── lucky_sup.erl lucky games supervisor 93 | │   ├── okey_sup.erl okey pre-created games 94 | │   └── tavla_sup.erl tavla pre-created games 95 | 96 | Aux libraries 97 | ------------- 98 | 99 | Useful libraries for shuflling cards/tashes/piles, logging and validation libraries. 100 | 101 | └── lib 102 |    ├── deck.erl shuffle machine 103 |    ├── gas.erl logging switcher 104 |    ├── known_records.erl strict allowed protocol records 105 |    └── midict.erl multi-indexed dictionary 106 | 107 | Size 108 | ---- 109 | 110 | Game Server is about 640KiB and 10K LOC of sources. 111 | 112 | License 113 | ------- 114 | 115 | For licensing Kakaranet Game Server for commercial use please contact: 116 | 117 | * Sinan Ustel 118 | * Ahmet Tez 119 | 120 | Authors 121 | ------- 122 | 123 | * Sergei Polkovnikov 124 | * Maksym Sokhatskyi 125 | 126 | OM A HUM 127 | -------------------------------------------------------------------------------- /apps/server/src/auth/anonymous.erl: -------------------------------------------------------------------------------- 1 | -module(anonymous). 2 | -compile(export_all). 3 | -include_lib("db/include/config.hrl"). 4 | -include_lib("kvs/include/user.hrl"). 5 | 6 | males() -> 7 | [ "alp","ayberk","doruk","mehmet","ozan","ali","musa","boran","isa", 8 | "dursun","uzay","taner","halit","yusuf","duman","serdar","halil", 9 | "emre","kadir","hasan","zeki","ihsan","rıza","kasım","ılgın" ]. 10 | 11 | females() -> 12 | [ "betül","eda","lale","pınar","filiz","ezgi","aysu","damla","konca", 13 | "oylum","ceren","ece","sevil","asena","jale","fatma","arzu","zeynep", 14 | "dilara","leyla","esra","irem","yasemin","bahar" ]. 15 | 16 | malesex() -> [ {male,X} || X <- males()]. 17 | femalesex() -> [ {female,X} || X <- females()]. 18 | names() -> malesex() ++ femalesex(). 19 | 20 | surnames() -> 21 | [ "özçelik","acar","özgür","özkan","tez","vural", 22 | "akbulut","arslan","avcı","ayhan","baştürk","çağlar","çelik", 23 | "çetinkaya","demir","dikmen","acar","doğan","ekinci","elmas", 24 | "erdem","erdoğan","güler","güneş","ilhan","inan","karaca", 25 | "karadağ","kaya","kemal","keskin","koç","korkmaz","mustafa", 26 | "osman","özbek","özcan","özdemir","özden","öztürk","paşa", 27 | "polat","sezer","şahin","sen","şimşek","tekin","tosun","tunç", 28 | "turan","ünal","yalçın","yazıcı","yıldırım","yılmaz" ]. 29 | 30 | ima_gio(N) -> {Id,Name,Surname,Sex} = lists:nth(N,imagionary_users()), Id. 31 | ima_gio(N,L) -> {Id,Name,Surname,Sex} = lists:nth(N,L), Id. 32 | 33 | imagionary_users() -> 34 | List = [ begin 35 | {Sex,X} = SX, 36 | [HX|TX] = X, NX = [tru(HX)] ++ TX, 37 | [HY|TY] = Y, NY = [tru(HY)] ++ TY, 38 | {wf:to_binary([ tr2en(Char) || Char <- unicode:characters_to_list(X++"_"++Y) ]), 39 | unicode:characters_to_binary(NX), 40 | unicode:characters_to_binary(NY),Sex} 41 | end || SX<-names(), Y<-surnames()], 42 | lists:keysort(1,List). 43 | 44 | % Erlang R16 45 | 46 | tru(252) -> 220; % $ü -> $Ü 47 | tru(351) -> 350; % $ş -> $Ş 48 | tru(246) -> 214; % $ö -> $Ö 49 | tru(305) -> 304; % $ı -> $İ 50 | tru(287) -> 286; % $ğ -> $Ğ 51 | tru(231) -> 199; % $ç -> $Ç 52 | tru(Ch) -> string:to_upper(Ch). 53 | 54 | tr2en(252) -> $u; 55 | tr2en(246) -> $o; 56 | tr2en(231) -> $c; 57 | tr2en(305) -> $i; 58 | tr2en(351) -> $s; 59 | tr2en(252) -> $u; 60 | tr2en(287) -> $g; 61 | tr2en(Sym) -> Sym. 62 | 63 | % Erlang R17 64 | 65 | %tru($ü) -> $Ü; 66 | %tru($ş) -> $Ş; 67 | %tru($ö) -> $Ö; 68 | %tru($ı) -> $İ; 69 | %tru($ğ) -> $Ğ; 70 | %tru($ç) -> $Ç; 71 | %tru(Ch) -> string:to_upper(Ch). 72 | 73 | %tr2en($ü) -> $u; 74 | %tr2en($ö) -> $o; 75 | %tr2en($ç) -> $c; 76 | %tr2en($ı) -> $i; 77 | %tr2en($ş) -> $u; 78 | %tr2en($ğ) -> $g; 79 | %tr2en(Sym) -> Sym. 80 | 81 | fake_id() -> 82 | FakeUsers = imagionary_users(), 83 | Pos = crypto:rand_uniform(1, length(FakeUsers)), 84 | H0 = ima_gio(Pos,FakeUsers), 85 | Id = wf:to_binary(wf:to_list(H0) ++ wf:to_list(id_generator:get_id2())). 86 | 87 | fake_id(Login) -> wf:to_binary(wf:to_list(Login) ++ wf:to_list(id_generator:get_id2())). 88 | 89 | create_users(A,B) -> 90 | ImagioUsers = imagionary_users(), 91 | [ begin 92 | {Id,Name,Surname,Sex} = lists:nth(N,ImagioUsers), 93 | U = #user{ username = Id, 94 | id = Id, 95 | names = Name, 96 | sex = Sex, 97 | surnames = Surname, 98 | birth={1981,9,29} }, kvs:put(U) end || N <- lists:seq(A, B) ]. 99 | 100 | virtual_users() -> 101 | case kvs:get(user,<<"imam@synrc.com">>) of 102 | {ok,_} -> skip; 103 | _ -> kvs:join(), create_users(1,100), kvs:put(#user{id= <<"imam@synrc.com">>}) end, 104 | 105 | AllUsers = imagionary_users(), 106 | F = fun({UserId,_,_,Sex}, Acc) -> 107 | User = auth_server:get_user_info_by_user_id(UserId), 108 | case User of 109 | {error,_} -> Acc; 110 | _ -> [UserId | Acc] 111 | end 112 | end, 113 | lists:usort(lists:foldl(F, [], AllUsers)). 114 | 115 | random_users(Num, AllUsers) -> 116 | AllUsersNum = length(AllUsers), 117 | random_users(Num, [], AllUsers, AllUsersNum). 118 | 119 | random_users(0, Acc, _AllUsers, _AllUsersNum) -> Acc; 120 | random_users(N, Acc, AllUsers, AllUsersNum) -> 121 | User = lists:nth(crypto:rand_uniform(1, AllUsersNum + 1), AllUsers), 122 | case lists:member(User, Acc) of 123 | false -> random_users(N - 1, [User | Acc], AllUsers, AllUsersNum); 124 | true -> random_users(N, Acc, AllUsers, AllUsersNum) 125 | end. 126 | -------------------------------------------------------------------------------- /apps/server/src/auth/auth_server.erl: -------------------------------------------------------------------------------- 1 | -module(auth_server). 2 | 3 | -include_lib("server/include/settings.hrl"). 4 | -include_lib("server/include/authtoken.hrl"). 5 | -include_lib("server/include/requests.hrl"). 6 | -include_lib("kvs/include/user.hrl"). 7 | 8 | -behaviour(gen_server). 9 | -compile(export_all). 10 | 11 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). 12 | 13 | -define(SERVER, ?MODULE). 14 | 15 | -define(SPARE_LOGINS, [ 16 | #'PlayerInfo'{name = <<"Hürrem"/utf8>>, sex = female, surname = <<"Sultan">>, login = <<"peace">>, robot = true}, 17 | #'PlayerInfo'{name = <<"Ilya">>, sex = male, surname = <<"Prigogine">>, login = <<"synergetics">>, robot = true}, 18 | #'PlayerInfo'{name = <<"Albert">>, sex = male, surname= <<"Einstein">>, login = <<"quantum">>, robot = true }, 19 | #'PlayerInfo'{name = <<"Marie">>, sex = female, surname= <<"Curie">>, login = <<"radio">>, robot = true } 20 | ]). 21 | 22 | -record(state, {spare = ?SPARE_LOGINS,tokens}). 23 | 24 | spare() -> [ P#'PlayerInfo'{id =wf:to_binary(wf:to_list(P#'PlayerInfo'.login) ++ 25 | wf:to_list(id_generator:get_id2()))} || P <- ?SPARE_LOGINS ]. 26 | 27 | start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 28 | store_token(GameId, Token, UserId) -> gen_server:call(?SERVER, {store_token, GameId, Token, UserId}). 29 | get_user_info(Token) -> gen_server:call(?SERVER, {get_user_info, Token}). 30 | get_user_info_by_user_id(UserId) -> user_info(UserId). 31 | generate_token(Game,User) -> T = base64:encode(crypto:rand_bytes(100)), store_token(Game,T,User). 32 | 33 | init([]) -> 34 | Tokens = ets:new(tokens, [private, ordered_set, {keypos, #authtoken.token}]), 35 | {ok, #state{tokens = Tokens}}. 36 | 37 | handle_call({store_token, GameId, Token, UserId}, _From, #state{tokens = E} = State) -> 38 | store_token(GameId, E, Token, UserId), 39 | {reply, Token, State}; 40 | 41 | handle_call({get_user_info, Token}, _From, #state{tokens = E} = State) -> 42 | case ets:lookup(E, Token) of 43 | [] -> 44 | gas:info(?MODULE,"Token not found. Denied.", []), 45 | {reply, false, State}; 46 | List -> 47 | {authtoken, _, UserId} = hd(List), 48 | Reply = case user_info(UserId) of 49 | {error, not_found} -> 50 | gas:info(?MODULE,"User is not in DB", []), 51 | user_info(#user{id = UserId, names= UserId, surnames = <<>> }); 52 | UserInfo -> 53 | gas:info(?MODULE,"Registered User", []), 54 | UserInfo 55 | end, 56 | {reply, Reply, State} 57 | end; 58 | 59 | handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. 60 | handle_cast(_Msg, State) -> {noreply, State}. 61 | handle_info(_Info, State) -> {noreply, State}. 62 | terminate(_Reason, _State) -> ok. 63 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 64 | 65 | robot_credentials() -> 66 | Pos = crypto:rand_uniform(1, length(?SPARE_LOGINS) + 1), 67 | H0 = lists:nth(Pos, ?SPARE_LOGINS), 68 | Id = wf:to_binary(wf:to_list(H0#'PlayerInfo'.login) ++ wf:to_list(id_generator:get_id2())), 69 | H0#'PlayerInfo'{id = Id}. 70 | 71 | store_token(GameId, E, Token, UserId) -> 72 | gas:info(?MODULE,"Storing token: ~p", [Token]), 73 | Data = #authtoken{token = Token, id = UserId}, 74 | ets:insert(E, Data). 75 | 76 | player_name(#'PlayerInfo'{login = Id, name = Name, surname = Surname}) -> 77 | wf:to_binary([case Name of <<"undefined">> -> Id; 78 | _ -> wf:to_list(Name) ++ case Surname of 79 | <<"undefined">> -> ""; _ -> " " ++ wf:to_list(Surname) end end]). 80 | 81 | user_info(#user{}=UserData) -> 82 | % gas:info(?MODULE,"PlayerInfo by #user: ~p",[UserData]), 83 | #'PlayerInfo'{id = UserData#user.id, 84 | login = UserData#user.username, 85 | name = UserData#user.names, 86 | sex = UserData#user.sex, 87 | avatar_url = UserData#user.avatar, 88 | skill = proplists:get_value(skill,UserData#user.tokens,0), 89 | score = proplists:get_value(score,UserData#user.tokens,0), 90 | surname = UserData#user.surnames}; 91 | 92 | 93 | user_info(UserId) -> 94 | case kvs:get(user,UserId) of 95 | {ok, UserData} -> 96 | % gas:info(?MODULE,"User Data: ~p",[UserData]), 97 | user_info(UserData); 98 | Error -> Error end. 99 | -------------------------------------------------------------------------------- /apps/server/src/game_app.erl: -------------------------------------------------------------------------------- 1 | -module(game_app). 2 | -behaviour(application). 3 | -export([start/2, start/0, stop/0, stop/1]). 4 | 5 | start() -> start(init,[]). 6 | start(_StartType, _StartArgs) -> game_sup:start_link(). 7 | stop() -> stop([]). 8 | stop(_State) -> game_sup:stop(). 9 | -------------------------------------------------------------------------------- /apps/server/src/game_sup.erl: -------------------------------------------------------------------------------- 1 | -module(game_sup). 2 | -behaviour(supervisor). 3 | -export([start_link/0, stop/0]). 4 | -export([init/1, start/0, start_game/3, stop_game/2]). 5 | -define(SERVER, ?MODULE). 6 | 7 | start() -> supervisor:start({local, ?SERVER}, ?MODULE, []). 8 | start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). 9 | stop() -> exit(?SERVER, shutdown). 10 | start_game(Sup,Mod,Par) -> supervisor:start_child(Sup,[Mod,Par]). 11 | stop_game(Sup,Pid) -> supervisor:terminate_child(Sup,Pid). 12 | 13 | init([]) -> 14 | RestartStrategy = one_for_one, 15 | MaxRestarts = 100, 16 | MaxSecondsBetweenRestarts = 3600, 17 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, 18 | Restart = permanent, 19 | Shutdown = 2000, 20 | IdGen = {id_generator, {id_generator, start_link, []}, Restart, Shutdown, worker, [id_generator]}, 21 | AuthServer = {auth_server, {auth_server, start_link, []}, Restart, Shutdown, worker, [auth_server]}, 22 | GameLog = {journal, {journal, start_link, []}, Restart, Shutdown, worker, [journal]}, 23 | TavlaSup = {tavla_sup, {tavla_sup, start_link, []}, Restart, Shutdown, supervisor, [tavla_sup]}, 24 | OkeySup = {okey_sup, {okey_sup, start_link, []}, Restart, Shutdown, supervisor, [okey_sup]}, 25 | LuckySup = {lucky_sup, {lucky_sup, start_link, []}, Restart, Shutdown, supervisor, [lucky_sup]}, 26 | {ok, {SupFlags, [IdGen,LuckySup,AuthServer,GameLog,TavlaSup,OkeySup]}}. 27 | 28 | -------------------------------------------------------------------------------- /apps/server/src/lib/deck.erl: -------------------------------------------------------------------------------- 1 | %% Author: Serge Polkovnikov 2 | %% Created: Oct 8, 2012 3 | %% Description: The library for manipulations with decks. 4 | -module(deck). 5 | 6 | 7 | %% Predefined types of deck: 8 | %% empty - No one element in the deck. 9 | %% 10 | %% okey - The deck consist of such types of element: {Color, Value} | false_okey. 11 | %% There are two instances of each element in the deck. 12 | %% Color = 1..4 13 | %% Value = 1..13 14 | 15 | %% 16 | %% Include files 17 | %% 18 | 19 | %% 20 | %% Exported Functions 21 | %% 22 | -export([ 23 | init_deck/1, 24 | from_list/1, 25 | to_list/1, 26 | shuffle/1, 27 | pop/2, 28 | push/2, 29 | get/2, 30 | put/3, 31 | replace/3, 32 | size/1, 33 | del_first/2, 34 | member/2 35 | ]). 36 | 37 | %% 38 | %% API Functions 39 | %% 40 | 41 | 42 | %% @spec init_deck(Type) -> Deck 43 | %% @doc Creates a deck. 44 | %% @end 45 | 46 | init_deck(empty) -> 47 | []; 48 | init_deck(okey) -> 49 | [false_okey, false_okey | [{C, V} || C <- [1,2,3,4], V <- lists:seq(1,13), _ <- [1,2]]]. 50 | 51 | 52 | %% @spec from_list(List) -> Deck 53 | %% @doc Creates a deck from the list of elements. 54 | %% @end 55 | 56 | from_list(List) -> 57 | List. 58 | 59 | 60 | %% @spec to_list(Deck) -> List 61 | %% @doc Convers the deck to a list of elements. 62 | %% @end 63 | 64 | to_list(Deck) -> 65 | Deck. 66 | 67 | %% @spec shuffle(Deck1) -> Deck2 68 | %% @doc Shuffles the deck. 69 | %% @end 70 | 71 | shuffle(Deck) when is_list(Deck) -> 72 | shuffle(Deck, deck:size(Deck), []). 73 | 74 | shuffle([], 0, Acc) -> Acc; 75 | shuffle(Deck, Size, Acc) -> 76 | Pos = crypto:rand_uniform(1, Size+1), 77 | {E, Deck1} = get(Pos, Deck), 78 | shuffle(Deck1, Size-1, [E |Acc]). 79 | 80 | 81 | %% @spec pop(Num, Deck1) -> {Deck2, Deck3} 82 | %% @doc Takes specified number of elements from top of the deck. 83 | %% @end 84 | 85 | pop(Num, Deck) when Num > 0, is_list(Deck) -> 86 | lists:split(Num, Deck). 87 | 88 | 89 | %% @spec push(Deck1, Deck2) -> Deck3. 90 | %% @doc Puts the first deck on the top of the second deck. 91 | %% @end 92 | 93 | push(Deck1, Deck2) when is_list(Deck1), is_list(Deck2) -> 94 | Deck1 ++ Deck2. 95 | 96 | 97 | %% @spec get(Pos, Deck1) -> {E, Deck2} 98 | %% @doc Draws an element at the position Pos of the deck. 99 | %% Position is counted from top of the deck. 100 | %% @end 101 | 102 | get(Pos, Deck) when Pos > 0, is_list(Deck) -> 103 | {Head, [E | Tail]} = lists:split(Pos - 1, Deck), 104 | {E, Head ++ Tail}. 105 | 106 | %% @spec put(E, Pos, Deck1) -> Deck2 107 | %% @doc Inserts the element to the position Pos of the deck. 108 | %% Position is counted from top of the deck. 109 | %% @end 110 | 111 | put(E, Pos, Deck) when Pos > 0, is_list(Deck) -> 112 | {Head, Tail} = lists:split(Pos - 1, Deck), 113 | Head ++ [E | Tail]. 114 | 115 | 116 | %% @spec replace(E1, E2, Deck1) -> Deck2 117 | %% @doc Replaces all instances of the element E1 in the deck by the element E2. 118 | %% @end 119 | 120 | replace(E1, E2, Deck) -> 121 | [if E == E1 -> E2; true -> E end || E <- Deck]. 122 | 123 | 124 | %% @spec size(Deck) -> Size 125 | %% @doc Returns a number of elements in the deck. 126 | %% @end 127 | 128 | size(Deck) when is_list(Deck) -> 129 | length(Deck). 130 | 131 | 132 | %% @spec del_first(E, Deck1) -> {ok, Deck2} | error 133 | %% @doc Deletes the element from the deck. 134 | %% @end 135 | 136 | del_first(E, Deck) -> 137 | case lists:member(E, Deck) of 138 | true -> 139 | {ok, lists:delete(E, Deck)}; 140 | false -> 141 | error 142 | end. 143 | 144 | %% @spec member(E, Deck) -> boolean() 145 | %% @doc Checks is the element a memeber of the deck. 146 | %% @end 147 | 148 | member(E, Deck) -> 149 | lists:member(E, Deck). 150 | %% 151 | %% Local Functions 152 | %% 153 | 154 | -------------------------------------------------------------------------------- /apps/server/src/lib/gas.erl: -------------------------------------------------------------------------------- 1 | -module(gas). 2 | -compile(export_all). 3 | 4 | log_modules() -> []. 5 | -define(ALLOWED, (wf:config(server,log_modules,gas))). 6 | 7 | log(Module, String, Args, Fun) -> 8 | case lists:member(Module,?ALLOWED:log_modules()) of 9 | true -> error_logger:Fun(String, Args); 10 | false -> skip end. 11 | 12 | info(Module,String, Args) -> log(Module,String, Args, info_msg). 13 | info(String, Args) -> log(?MODULE, String, Args, info_msg). 14 | info(String) -> log(?MODULE, String, [], info_msg). 15 | 16 | warning(Module,String, Args) -> log(Module, String, Args, warning_msg). 17 | warning(String, Args) -> log(?MODULE, String, Args, warning_msg). 18 | warning(String) -> log(?MODULE,String, [], warning_msg). 19 | 20 | error(Module,String, Args) -> log(Module, String, Args, error_msg). 21 | error(String, Args) -> log(?MODULE, String, Args, error_msg). 22 | error(String) -> log(?MODULE, String, [], error_msg). 23 | -------------------------------------------------------------------------------- /apps/server/src/okey/okey_test.erl: -------------------------------------------------------------------------------- 1 | -module(okey_test). 2 | -compile(export_all). 3 | -include_lib("server/include/requests.hrl"). 4 | -include_lib("server/include/settings.hrl"). 5 | -include_lib("kvs/include/user.hrl"). 6 | 7 | -define(GAMEID, 1000001). 8 | %-define(GAMEID, 5000220). 9 | 10 | main() -> 11 | User = #user{id = <<"testbot@bot.net">>, username = <<"testbot">>, surnames = [<<"bottest">>]}, 12 | 13 | kvs:add(User), 14 | PlayerInfo = auth_server:user_info(<<"testbot@bot.net">>), 15 | %% gas:info(?MODULE, "TEST BOT player info ~p", [PlayerInfo]), 16 | 17 | {ok, BotPid} = okey_bot:start_link(self(), PlayerInfo, ?GAMEID), 18 | erlang:monitor(process, BotPid), 19 | 20 | okey_bot:join_game(BotPid), 21 | 22 | loop(BotPid). 23 | 24 | loop(BotPid) -> 25 | gas:info("TEST OKEY NG listen to air"), 26 | 27 | receive 28 | {'DOWN', _MonitorRef, _Type, _Object, _Info} -> ok; 29 | UnknowMsg -> wf:info("Unknow message: ~p", [UnknowMsg]), loop(BotPid) 30 | end. 31 | -------------------------------------------------------------------------------- /apps/server/src/server.app.src: -------------------------------------------------------------------------------- 1 | {application, server, 2 | [ 3 | {description, "KKN Game Server"}, 4 | {vsn, "3"}, 5 | {registered, []}, 6 | {applications, [kernel,gproc,stdlib,db]}, 7 | {mod, { game_app, []}}, 8 | {env, []} 9 | ]}. 10 | -------------------------------------------------------------------------------- /apps/server/src/sup/id_generator.erl: -------------------------------------------------------------------------------- 1 | -module(id_generator). 2 | -behaviour(gen_server). 3 | -export([start_link/0]). 4 | -export([get_id/0,get_id2/0]). 5 | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). 6 | -define(SERVER, ?MODULE). 7 | -record(state, {lastid = 1000000, robotid = 1500000}). 8 | 9 | get_id() -> gen_server:call(?MODULE, get_id). 10 | get_id2() -> gen_server:call(?MODULE, get_id2). 11 | start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 12 | 13 | init([]) -> {ok, #state{lastid=wf:config(nsx_idgen,game_pool,1000000)}}. 14 | handle_call(get_id, _From, #state{lastid = LID} = State) -> Reply = LID + 1, {reply, Reply, State#state{lastid = Reply}}; 15 | handle_call(get_id2, _From, #state{robotid = RID} = State) -> Reply = RID + 1, {reply, Reply, State#state{robotid = Reply}}; 16 | handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}. 17 | handle_cast(_Msg, State) -> {noreply, State}. 18 | handle_info(_Info, State) -> {noreply, State}. 19 | terminate(_Reason, _State) -> ok. 20 | code_change(_OldVsn, State, _Extra) -> {ok, State}. 21 | -------------------------------------------------------------------------------- /apps/server/src/sup/lucky_sup.erl: -------------------------------------------------------------------------------- 1 | -module(lucky_sup). 2 | -behaviour(supervisor). 3 | -include_lib("db/include/config.hrl"). 4 | -export([start_link/0]). 5 | -export([init/1]). 6 | -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}). 7 | 8 | start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). 9 | 10 | init([]) -> 11 | RestartStrategy = one_for_one, 12 | MaxRestarts = 1000, 13 | MaxSecondsBetweenRestarts = 1, 14 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, 15 | Restart = permanent, 16 | Shutdown = 2000, 17 | 18 | OkeyTableParams = [{mult_factor, 1}, 19 | {slang_allowed, false}, 20 | {observers_allowed, false}, 21 | {tournament_type, lucky}, 22 | {round_timeout, infinity}, 23 | %%% {round_timeout, 30 * 1000}, 24 | {set_timeout, infinity}, 25 | %%% {set_timeout, 10 * 60 *1000}, 26 | {speed, normal}, 27 | {game_type, standard}, 28 | {rounds, undefined}, 29 | {reveal_confirmation, false}, 30 | {next_series_confirmation, no}, 31 | {pause_mode, normal}, 32 | {social_actions_enabled, true}, 33 | {gosterge_finish_allowed, undefined} 34 | ], 35 | OkeyGameId = id_generator:get_id(), 36 | GameName = "I'm filling lucky - " ++ erlang:integer_to_list(OkeyGameId), 37 | OkeyParams = [{game, game_okey}, 38 | {game_mode, standard}, 39 | {game_name, GameName}, 40 | {mode, normal}, % Common table for several real players 41 | {seats, 4}, 42 | %%% {quota_per_round, Quota}, 43 | {table_module, okey_table}, 44 | {bot_module, okey_bot}, 45 | {table_params, OkeyTableParams} 46 | ], 47 | OkeySpec = {okey_lucky, {lucky, start_link, [OkeyGameId, OkeyParams]}, 48 | Restart, Shutdown, worker, [lucky]}, 49 | 50 | 51 | TavlaTableParams = [{mult_factor, 1}, 52 | {slang_allowed, false}, 53 | {observers_allowed, false}, 54 | {tournament_type, lucky}, 55 | {round_timeout, infinity}, 56 | %%% {round_timeout, 30 * 1000}, 57 | {set_timeout, infinity}, 58 | %%% {set_timeout, 10 * 60 *1000}, 59 | {speed, normal}, 60 | {game_mode, standard}, 61 | {rounds, undefined}, 62 | {next_series_confirmation, no}, 63 | {pause_mode, normal}, 64 | {social_actions_enabled, true} 65 | ], 66 | 67 | TavlaGameId = id_generator:get_id(), 68 | TavlaGameName = "I'm filling lucky - " ++ erlang:integer_to_list(TavlaGameId), 69 | TavlaParams = [{game, game_tavla}, 70 | {game_mode, standard}, 71 | {game_name, TavlaGameName}, 72 | {mode, normal}, % Common table for several real players 73 | {seats, 2}, 74 | %%% {quota_per_round, Quota}, 75 | {table_module, tavla_table}, 76 | {bot_module, tavla_bot}, 77 | {table_params, TavlaTableParams} 78 | ], 79 | TavlaSpec = {tavla_lucky, {lucky, start_link, [TavlaGameId, TavlaParams]}, 80 | Restart, Shutdown, worker, [lucky]}, 81 | 82 | {ok, { SupFlags, [OkeySpec, TavlaSpec]} }. 83 | 84 | -------------------------------------------------------------------------------- /apps/server/src/sup/okey_sup.erl: -------------------------------------------------------------------------------- 1 | -module(okey_sup). 2 | -behaviour(supervisor). 3 | -export([start_link/0, stop/0]). 4 | -export([init/1, start/0, start_game/3]). 5 | -define(SERVER, ?MODULE). 6 | 7 | -define(CROWD_STANDALONE_3PL_NUM, 15). 8 | 9 | start() -> supervisor:start({local, ?SERVER}, ?MODULE, []). 10 | start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). 11 | stop() -> exit(?SERVER, shutdown). 12 | start_game(Mod,Par,GameId) -> 13 | gas:info(?MODULE,"OKEY SUP START CHILD"), 14 | Restart = transient, 15 | Shutdown = 200, 16 | ChildSpec = {GameId, {Mod, start_link, Par}, Restart, Shutdown, worker, [Mod]}, 17 | supervisor:start_child(?MODULE,ChildSpec). 18 | 19 | init([]) -> 20 | gas:info(?MODULE,"OKEY SUP STARTED"), 21 | RestartStrategy = one_for_one, 22 | MaxRestarts = 1, 23 | MaxSecondsBetweenRestarts = 600, 24 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, 25 | Specs = okey_standalone_specs(?CROWD_STANDALONE_3PL_NUM, 3), 26 | {ok, {SupFlags, Specs}}. 27 | 28 | 29 | okey_standalone_specs(GamesNum, VirtUsersPerTable) -> 30 | VirtualUsers = anonymous:virtual_users(), 31 | if length(VirtualUsers) < VirtUsersPerTable -> 32 | []; 33 | true -> 34 | F = fun(_) -> 35 | GameId = game:gen_game_id(), 36 | GameName = "Okey/Crowd game - " ++ erlang:integer_to_list(GameId), 37 | Users = anonymous:random_users(VirtUsersPerTable, VirtualUsers), 38 | %%% Users = [robot, robot, robot], 39 | TableParams = [ 40 | {table_name, ""}, 41 | {mult_factor, 1}, 42 | {slang_allowed, false}, 43 | {observers_allowed, false}, 44 | {tournament_type, standalone}, 45 | {round_timeout, infinity}, 46 | {set_timeout, infinity}, 47 | {speed, fast}, 48 | {game_type, standard}, 49 | {rounds, lists:nth(crypto:rand_uniform(1,4),okey_scoring:rounds())}, 50 | {reveal_confirmation, true}, 51 | {next_series_confirmation, no_exit}, 52 | {pause_mode, normal}, 53 | {social_actions_enabled, true}, 54 | {gosterge_finish_allowed, undefined} 55 | ], 56 | CommonParams = [{speed, fast}, 57 | {rounds, lists:nth(crypto:rand_uniform(1,4),okey_scoring:rounds())}, 58 | {double_points, 1}, 59 | {game_mode,standard}, 60 | {slang, false}, 61 | {observers, false}, 62 | {owner,"maxim"} 63 | ], 64 | Params = [{game, game_okey}, 65 | {game_mode, standard}, 66 | {game_name, GameName}, 67 | {seats, 4}, 68 | {registrants, Users}, 69 | {initial_points, 0}, 70 | {quota_per_round, 1}, 71 | {kakush_for_winners, 1}, 72 | {kakush_for_loser, 1}, 73 | {win_game_points, 1}, 74 | {mul_factor, 1}, 75 | {table_module, okey_table}, 76 | {bot_module, okey_bot}, 77 | {bots_replacement_mode, enabled}, 78 | {table_params, TableParams}, 79 | {common_params, CommonParams} %% This data will used for the gproc register 80 | ], 81 | {GameId, 82 | {standalone, start_link, [GameId, Params]}, 83 | _Restart = permanent, _Shutdown = 2000, worker, [standalone]} 84 | end, 85 | lists:map(F, lists:seq(1, GamesNum)) 86 | end. 87 | 88 | -------------------------------------------------------------------------------- /apps/server/src/sup/tavla_sup.erl: -------------------------------------------------------------------------------- 1 | -module(tavla_sup). 2 | -behaviour(supervisor). 3 | -export([start_link/0, stop/0]). 4 | -export([init/1, start/0, start_game/3]). 5 | -define(SERVER, ?MODULE). 6 | 7 | -define(CROWD_STANDALONE_1PL_NUM, 15). 8 | 9 | start() -> supervisor:start({local, ?SERVER}, ?MODULE, []). 10 | start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). 11 | stop() -> exit(?SERVER, shutdown). 12 | start_game(Mod,Par,GameId) -> 13 | gas:info(?MODULE,"TAVLA SUP STAR CHILD"), 14 | %% Restart = transient, 15 | Restart = temporary, 16 | Shutdown = 200, 17 | ChildSpec = {GameId, {Mod, start_link, Par}, Restart, Shutdown, worker, [Mod]}, 18 | supervisor:start_child(?MODULE,ChildSpec). 19 | 20 | init([]) -> 21 | gas:info(?MODULE,"TAVLA SUP STARTED"), 22 | RestartStrategy = one_for_one, 23 | MaxRestarts = 1, 24 | MaxSecondsBetweenRestarts = 600, 25 | SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts}, 26 | Specs = tavla_standalone_specs(?CROWD_STANDALONE_1PL_NUM, 1), 27 | {ok, {SupFlags, Specs}}. 28 | 29 | tavla_standalone_specs(GamesNum, VirtUsersPerTable) -> 30 | VirtualUsers = anonymous:virtual_users(), 31 | if length(VirtualUsers) < VirtUsersPerTable -> 32 | []; 33 | true -> 34 | F = fun(_) -> 35 | GameId = game:gen_game_id(), 36 | GameName = "Tavla/Crowd game: " ++ erlang:integer_to_list(GameId), 37 | Users = anonymous:random_users(VirtUsersPerTable, VirtualUsers), 38 | %%% Users = [robot], 39 | TableParams = [ 40 | {table_name, ""}, 41 | {mult_factor, 1}, 42 | {slang_allowed, false}, 43 | {observers_allowed, false}, 44 | {tournament_type, standalone}, 45 | {round_timeout, infinity}, 46 | {set_timeout, infinity}, 47 | {speed, fast}, 48 | {game_mode, standard}, 49 | {rounds, 3}, 50 | {next_series_confirmation, no_exit}, 51 | {pause_mode, normal}, 52 | {social_actions_enabled, true}, 53 | {tables_num, 1} 54 | ], 55 | CommonParams = [{speed, fast}, 56 | {rounds, 3}, 57 | {double_points, 1}, 58 | {game_mode,standard}, 59 | {speed, normal}, 60 | {slang, false}, 61 | {observers, false}, 62 | {owner,"maxim"} 63 | ], 64 | Params = [{game, game_tavla}, 65 | {game_mode, standard}, 66 | {game_name, GameName}, 67 | {seats, 2}, 68 | {registrants, Users}, 69 | {initial_points, 0}, 70 | {quota_per_round, 1}, 71 | {kakush_for_winners, 1}, 72 | {kakush_for_loser, 1}, 73 | {win_game_points, 1}, 74 | {mul_factor, 1}, 75 | {table_module, tavla_table}, 76 | {bot_module, tavla_bot}, 77 | {bots_replacement_mode, enabled}, 78 | {table_params, TableParams}, 79 | {common_params, CommonParams} 80 | ], 81 | {GameId, 82 | {standalone, start_link, [GameId, Params]}, 83 | _Restart = permanent, _Shutdown = 2000, worker, [standalone]} 84 | end, 85 | lists:map(F, lists:seq(1, GamesNum)) 86 | end. 87 | 88 | -------------------------------------------------------------------------------- /apps/server/src/tavla/tavla_lib.erl: -------------------------------------------------------------------------------- 1 | %% Author: serge 2 | %% Created: Feb 14, 2013 3 | %% Description: TODO: Add description to game_tavla_lib 4 | -module(tavla_lib). 5 | 6 | %% 7 | %% Include files 8 | %% 9 | 10 | %% 11 | %% Exported Functions 12 | %% 13 | -export([board_to_text1/1, 14 | board_to_text2/1, 15 | board_to_text3/1, 16 | board_to_text4/1]). 17 | 18 | -define(BLACK, black). 19 | -define(WHITE, white). 20 | -define(WHITE_OUT, wo). 21 | -define(WHITE_BAR, wb). 22 | -define(BLACK_OUT, bo). 23 | -define(BLACK_BAR, bb). 24 | 25 | %% 26 | %% API Functions 27 | %% 28 | board_to_text1(Board) -> 29 | Str1 = "13 14 15 16 17 18 BB 19 20 21 22 23 24 BO", 30 | Str4 = " || ", 31 | Str7 = "12 11 10 09 08 07 WB 06 05 04 03 02 01 WO", 32 | List1 = [13, 14, 15, 16, 17, 18, ?BLACK_BAR, 19, 20 ,21, 22, 23, 24, ?BLACK_OUT], 33 | List2 = [12, 11, 10, 09, 08, 07, ?WHITE_BAR, 06, 05 ,04, 03, 02, 01, ?WHITE_OUT], 34 | Str2 = list_to_colors(List1, Board), 35 | Str3 = list_to_checkers(List1, Board), 36 | Str5 = list_to_checkers(List2, Board), 37 | Str6 = list_to_colors(List2, Board), 38 | [Str1, Str2, Str3, Str4, Str5, Str6, Str7]. 39 | 40 | board_to_text2(Board) -> 41 | Str1 = "|xoxoxo-xoxoxo| ", 42 | Str4 = "|------+------| ", 43 | Str7 = "|xoxoxo-xoxoxo| ", 44 | List1 = [13, 14, 15, 16, 17, 18, ?BLACK_BAR, 19, 20 ,21, 22, 23, 24], 45 | List2 = [12, 11, 10, 09, 08, 07, ?WHITE_BAR, 06, 05 ,04, 03, 02, 01], 46 | Str2 = "|" ++ list_to_colors2(List1, Board) ++ "|" ++ list_to_colors2([?BLACK_OUT], Board), 47 | Str3 = "|" ++ list_to_checkers2(List1, Board) ++ "|" ++ list_to_checkers2([?BLACK_OUT], Board), 48 | Str5 = "|" ++ list_to_checkers2(List2, Board) ++ "|" ++ list_to_checkers2([?WHITE_OUT], Board), 49 | Str6 = "|" ++ list_to_colors2(List2, Board) ++ "|" ++ list_to_colors2([?WHITE_OUT], Board), 50 | [Str1, Str2, Str3, Str4, Str5, Str6, Str7]. 51 | 52 | board_to_text3(Board) -> 53 | ["(" ++ checker(?WHITE_OUT, Board)++ ")" ++ checker(?BLACK_BAR, Board) ++ 54 | "|" ++ quater(1, 6, Board) ++ "|" ++ quater(7, 12, Board) ++ "|" ++ 55 | quater(13, 18, Board) ++ "|" ++ quater(19, 24, Board) ++ "|" ++ 56 | checker(?WHITE_BAR, Board) ++ "(" ++ checker(?BLACK_OUT, Board) ++ ")"]. 57 | 58 | board_to_text4(Board) -> 59 | Str1 = "|xoxoxo+-+xoxoxo| ", 60 | Str4 = "|------|+|------| ", 61 | Str7 = "|xoxoxo+-+xoxoxo| ", 62 | List1 = [13, 14, 15, 16, 17, 18], 63 | List2 = [19, 20 ,21, 22, 23, 24], 64 | List3 = [12, 11, 10, 09, 08, 07], 65 | List4 = [06, 05 ,04, 03, 02, 01], 66 | Str2 = "|" ++ list_to_colors2(List1, Board) ++ "|" ++ list_to_colors2([?BLACK_BAR], Board) ++ "|" ++ list_to_colors2(List2, Board) ++ "|" ++ list_to_colors2([?BLACK_OUT], Board), 67 | Str3 = "|" ++ list_to_checkers2(List1, Board) ++ "|" ++ list_to_checkers2([?BLACK_BAR], Board) ++ "|" ++ list_to_checkers2(List2, Board) ++ "|" ++ list_to_checkers2([?BLACK_OUT], Board), 68 | Str5 = "|" ++ list_to_checkers2(List3, Board) ++ "|" ++ list_to_checkers2([?WHITE_BAR], Board) ++ "|" ++ list_to_checkers2(List4, Board)++ "|" ++ list_to_checkers2([?WHITE_OUT], Board), 69 | Str6 = "|" ++ list_to_colors2(List3, Board) ++ "|" ++ list_to_colors2([?WHITE_BAR], Board) ++ "|" ++ list_to_colors2(List4, Board)++ "|" ++ list_to_colors2([?WHITE_OUT], Board), 70 | [Str1, Str2, Str3, Str4, Str5, Str6, Str7]. 71 | 72 | get_checkers(Pos, Board) -> 73 | {_, Value} = lists:keyfind(Pos, 1, Board), 74 | Value. 75 | 76 | %% 77 | %% Local Functions 78 | %% 79 | 80 | list_to_checkers(List, Board) -> 81 | F = fun(Pos) -> 82 | case get_checkers(Pos, Board) of 83 | empty -> " "; 84 | {_, Num} -> io_lib:format("~2b ", [Num]) 85 | end 86 | end, 87 | lists:flatmap(F, List). 88 | 89 | list_to_colors(List, Board) -> 90 | F = fun(Pos) -> 91 | case get_checkers(Pos, Board) of 92 | empty -> " "; 93 | {?WHITE, _} -> " W "; 94 | {?BLACK, _} -> " B " 95 | end 96 | end, 97 | lists:flatmap(F, List). 98 | 99 | list_to_checkers2(List, Board) -> 100 | F = fun(Pos) -> 101 | case get_checkers(Pos, Board) of 102 | empty -> " "; 103 | {_, Num} -> io_lib:format("~.16b", [Num]) 104 | end 105 | end, 106 | lists:flatmap(F, List). 107 | 108 | list_to_colors2(List, Board) -> 109 | [case get_checkers(Pos, Board) of 110 | empty -> " "; 111 | {?WHITE, _} -> "W"; 112 | {?BLACK, _} -> "B" 113 | end || Pos <- List]. 114 | 115 | checker(Pos, Board) -> 116 | case get_checkers(Pos, Board) of 117 | empty -> " "; 118 | {?WHITE, Num} -> "W" ++ io_lib:format("~.16b", [Num]); 119 | {?BLACK, Num} -> "B" ++ io_lib:format("~.16b", [Num]) 120 | end. 121 | 122 | quater(First, Last, Board)-> 123 | F = fun(Pos) -> "-" ++ checker(Pos, Board) end, 124 | lists:flatmap(F, lists:seq(First, Last)) ++ "-". 125 | -------------------------------------------------------------------------------- /apps/web/README.md: -------------------------------------------------------------------------------- 1 | Kakaranet Front Application 2 | =========================== 3 | 4 | Web App Skeleton for Voxoz PaaS 5 | -------------------------------------------------------------------------------- /apps/web/include/basic_types.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(BASIC_TYPES). 2 | -define(BASIC_TYPES, true). 3 | 4 | -type 'PlayerId'() :: any(). 5 | -type 'GameId'() :: any(). 6 | 7 | -record('PlayerInfo', { 8 | id :: 'PlayerId'(), 9 | login :: string(), 10 | name :: string(), 11 | surname :: string(), 12 | seat_num, 13 | connected, 14 | age :: integer(), 15 | skill :: integer(), 16 | score :: integer(), 17 | avatar_url :: string(), 18 | robot = false :: boolean(), 19 | sex }). 20 | 21 | -endif. 22 | -------------------------------------------------------------------------------- /apps/web/include/journal.hrl: -------------------------------------------------------------------------------- 1 | -ifndef(JOURNAL_HRL). 2 | -define(JOURNAL_HRL, "journal.hrl"). 3 | 4 | -include_lib("kvs/include/kvs.hrl"). 5 | -define(LOG_HEADER, game_id, date, time, user, module, type, speed, rounds). 6 | -define(CONTAINER_LOG, ?CONTAINER, ?LOG_HEADER, stats=[]). 7 | -record(container_log, {?CONTAINER_LOG}). 8 | -record(container_event, {?ITERATOR(container_log), ?LOG_HEADER }). 9 | 10 | -record(series_log, {?CONTAINER_LOG, score, game_points, kakush }). 11 | -record(series_event, {?ITERATOR(series_log),?LOG_HEADER, result, score}). 12 | 13 | -record(reveal_log, {?CONTAINER_LOG, skill, score }). 14 | -record(reveal_event, {?ITERATOR(reveal_log), ?LOG_HEADER, reason, winner, score, total}). 15 | 16 | -record(protocol_log, {?CONTAINER_LOG}). 17 | -record(protocol_event, {?ITERATOR(protocol_log), ?LOG_HEADER, event, game_event}). 18 | 19 | -endif. 20 | -------------------------------------------------------------------------------- /apps/web/include/requests.hrl: -------------------------------------------------------------------------------- 1 | -include("basic_types.hrl"). 2 | 3 | % actions are being instantiated from client to server 4 | 5 | -record(session_attach, {token}). 6 | -record(login, {username, password}). 7 | -record(logout, {}). 8 | -record(join_game, {game}). 9 | -record(stats_action, {player_id :: 'PlayerId'(), game_type}). 10 | -record(chat, {game, player_id, who, message }). 11 | -record(game_action, {game :: 'GameId'(), action, args = []}). 12 | -record(pause_game, {table_id :: integer(),game :: 'GameId'(),action}). 13 | 14 | % event notifications from server to client 15 | 16 | -record(game_event, {game :: 'GameId'(), event, args = [] }). 17 | -record(chat_event, {game :: 'GameId'(), player_id :: 'PlayerId'(), who, message }). 18 | -record(stats_event, {player_id :: 'PlayerId'(), games, reveals, protocol, score}). 19 | -record(game_paused, {table_id :: integer(), game :: 'GameId'(),action,who :: 'PlayerId'(),retries}). 20 | -record(disconnect, {reason_id,reason}). 21 | -record(player_left, {player :: 'PlayerId'(),human_replaced=false,replacement::'PlayerId'()}). 22 | -------------------------------------------------------------------------------- /apps/web/include/settings.hrl: -------------------------------------------------------------------------------- 1 | -define(LOBBY_TIMEOUT, begin {ok, X} = kvs:get(config,"games/lobby_timeout", 60000), X end). 2 | -define(REMATCH_TIMEOUT, begin {ok, X} = kvs:get(config,"games/rematch_timeout", 45000), X end). 3 | -define(ROBOT_DELAY, begin {ok, X} = kvs:get(config,"games/okey/robot_delay", 500), X end). 4 | -define(SKILL_SEED, begin {ok, X} = kvs:get(config, "games/okey/skill_seed", 500), X end). %% skill value for new player 5 | -define(IS_TEST, begin {ok, X} = kvs:get(config,"debug/is_test", false), X end). %% determines if code is executed in test 6 | -define(ALL_SESSIONS, <<"all_sessions_topic">>). 7 | -define(TEST_TOKEN, "EBAs6dg2Xw6XuCdg8qiPmlBLgYJ6N4Ti0P+oGpWgYz4NW4nBBUzTe/wAuLYtPnjFpsjCExxSpV78fipmsPxcf+NGy+QKIM6rmVJhpnIlKf0bpFNuGaAPjZAWthhGO8nZ0V8UnA=="). 8 | -define(TEST_TOKEN2, "EBAs6dg2Xw6XuCdg8qiPmlBLgYJ6N4Ti0P+oGpWgYz4NW4nBBUzTe/wAuLYtPnjFpsjCExxSpV78fipmsPxcf+NGy+QKIM6rmVJhpnIlKf0bpFNuGaAPjZAWthhGO8nZ0V8Un2=="). 9 | -define(PAIRED_TAVLA_ASYNC, false). 10 | -define(SOCIAL_ACTION_SUBSCRIBE, 1). 11 | -define(SOCIAL_ACTION_UNSUBSCRIBE, 2). 12 | -define(SOCIAL_ACTION_BLOCK, 3). 13 | -define(SOCIAL_ACTION_UNBLOCK, 4). 14 | -define(SOCIAL_ACTION_LOVE, 5). 15 | -define(SOCIAL_ACTION_HAMMER, 6). 16 | -------------------------------------------------------------------------------- /apps/web/include/users.hrl: -------------------------------------------------------------------------------- 1 | -record(user, {id, name, email, proplist = [{facebook, udefined}, 2 | {github, "github.com/b0oh"}, 3 | {local, undefined}, 4 | {twitter, udefined}], 5 | string = "common", 6 | number = 12, 7 | list_of_strings = ["one", "two", "three"], 8 | list_of_numbers = [34958726345, 12], 9 | nested_proplists = [{nested, [{number, 12}, 10 | {string, "common"}, 11 | {list_of_strings, ["one", "two", "three"]}, 12 | {list_of_atoms, [one, two, three]}, 13 | {list_of_numbers, [100000, 2,3 ]}]}]}). 14 | -------------------------------------------------------------------------------- /apps/web/priv/static/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devaspot/games/a1f7c3169c53d31e56049e90e0094a3f309603ae/apps/web/priv/static/README.md -------------------------------------------------------------------------------- /apps/web/priv/static/app/SVG.txt: -------------------------------------------------------------------------------- 1 | 2 | HOW TO PATCH SVG 3 | ================ 4 | 5 | MANUAL 6 | ------ 7 | 8 | 1. Clipping Regions for Sidebars 9 | -------------------------------- 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 2. foreignObject TextEditors for Chat 18 | ------------------------------------- 19 | 20 | 21 |
Write here some text.
24 |
25 | 26 | 27 |
Write here some text.
30 |
31 | 32 | JavaScript 33 | ---------- 34 | 35 | 3. Chat Containers (insert after Settings element for Z-order) 36 | -------------------------------------------------------------- 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/channel.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/css/main.css: -------------------------------------------------------------------------------- 1 | 2 | div { -webkit-user-select: text; user-select: text; } 3 | html, body { -webkit-user-select: none; user-select: none; } 4 | html, body { margin: 0; padding:0; overflow: hidden; width: 100%; height: 100%; font-family: "Exo 2"; } 5 | text { cursor: default; } 6 | .card text { cursor: inherit; } 7 | .card { 8 | -ms-touch-action : none; 9 | touch-action : none; 10 | -ms-scroll-chaining: none; 11 | -ms-scroll-limit: 0 0 0 0; 12 | } 13 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Kakaranet Okey 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/js/controller.js: -------------------------------------------------------------------------------- 1 | function ControllerScope(scope) { 2 | 3 | function Controller() { 4 | this.proxyAll(); 5 | this.refreshElements(); 6 | } 7 | 8 | var isIE = window.navigator.msPointerEnabled; 9 | 10 | $.extend(Controller.prototype, { 11 | proxy: function(func) { return func.bind(this); }, 12 | proxyAll: function() { 13 | if (this.proxies) for (var method, i = this.proxies.length; i--; ) method = this.proxies[i], 14 | this[method] = this.proxy(this[method]); 15 | }, 16 | withDelay: function(func, timeout) { return setTimeout(this.proxy(func), timeout || 0); }, 17 | $: function(selector) { return this.$el.find(selector); }, 18 | refreshElements: function() { if (this.elements) for (var element in this.elements) this[element] = this.$(this.elements[element]); }, 19 | on: function(eventType, handler) { return this.$el.on(eventType, handler), this; }, 20 | off: function(eventType, handler) { return this.$el.off(eventType, handler), this; }, 21 | clientX: function(e) { return isIE ? e.pageX : document.createTouch ? e.changedTouches[0].clientX : e.clientX; }, 22 | clientY: function(e) { return isIE ? e.pageY : document.createTouch ? e.changedTouches[0].clientY : e.clientY; }, 23 | intersect: function($el) { 24 | // var pos = this.$el.position(); 25 | // return pos.top < y && pos.bottom > y && pos.left < x && pos.right > x; 26 | var d0 = this.$el.position() 27 | , d1 = $el.position() 28 | , x11 = d0.left 29 | , y11 = d0.top 30 | , x12 = d0.left + this.$el.width() 31 | , y12 = d0.top + this.$el.height() 32 | , x21 = d1.left 33 | , y21 = d1.top 34 | , x22 = d1.left + $el.width() 35 | , y22 = d1.top + $el.height() 36 | , x_overlap = Math.max(0, Math.min(x12,x22) - Math.max(x11,x21)) 37 | , y_overlap = Math.max(0, Math.min(y12,y22) - Math.max(y11,y21)) 38 | return { 39 | square: x_overlap * y_overlap 40 | , res : x_overlap * y_overlap > $el.width() * $el.height() * .25 41 | } 42 | } 43 | }), 44 | 45 | scope.Controller = Controller; 46 | } 47 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/js/n2o/bullet.js: -------------------------------------------------------------------------------- 1 | function bullet(url) { 2 | 3 | var CONNECTING = 0; 4 | var OPEN = 1; 5 | var CLOSING = 2; 6 | var CLOSED = 3; 7 | 8 | var transports = { 9 | 10 | websocket: function() { 11 | var transport = null; 12 | if (window.WebSocket) { transport = window.WebSocket; } 13 | if (window.MozWebSocket) { transport = window.MozWebSocket; } 14 | if (transport) { return {'heart': true, 'transport': transport}; } 15 | return null; 16 | } 17 | 18 | }; 19 | 20 | var tn = 0; 21 | function next() { 22 | var c = 0; 23 | 24 | for (var f in transports) { 25 | if (tn == c) { 26 | var t = transports[f](); 27 | if (t) { var ret = new t.transport(url); ret.heart = t.heart; return ret; } 28 | tn++; 29 | } 30 | c++; 31 | } 32 | return false; 33 | } 34 | 35 | var stream = new function() { 36 | var isClosed = true; 37 | var readyState = CLOSED; 38 | var heartbeat; 39 | var delay = 80; 40 | var delayDefault = 80; 41 | var delayMax = 10000; 42 | 43 | var transport; 44 | 45 | 46 | 47 | this.transport = function () { return transport; } 48 | 49 | function init() { 50 | 51 | isClosed = false; 52 | readyState = CONNECTING; 53 | transport = next(); 54 | 55 | if (!transport) { 56 | delay = delayDefault; 57 | tn = 0; 58 | stream.ondisconnect(); 59 | setTimeout(function(){init();}, delayMax); 60 | return false; 61 | } 62 | 63 | transport.onopen = function() { 64 | delay = delayDefault; 65 | 66 | if (transport.heart) { 67 | heartbeat = setInterval(function(){stream.onheartbeat();}, 4000); 68 | } 69 | 70 | if (readyState != OPEN) { readyState = OPEN; stream.onopen(); } 71 | }; 72 | 73 | transport.onclose = function() { 74 | if (isClosed) { return; } 75 | 76 | transport = null; 77 | clearInterval(heartbeat); 78 | 79 | if (readyState == CLOSING){ 80 | readyState = CLOSED; 81 | stream.onclose(); 82 | } else { 83 | if (readyState == CONNECTING) tn++; 84 | delay *= 2; 85 | if (delay > delayMax) { delay = delayMax; } 86 | isClosed = true; 87 | setTimeout(function() { init(); }, delay); 88 | } 89 | }; 90 | transport.onerror = transport.onclose; 91 | transport.onmessage = function(e){ 92 | stream.onmessage(e); 93 | }; 94 | } 95 | 96 | init(); 97 | 98 | this.onopen = function(){}; this.oninit = function(){}; 99 | this.onmessage = function(){}; this.ondisconnect = function(){ initialized = false; }; 100 | this.onclose = function(){}; this.onheartbeat = function(){ return this.send('PING'); }; 101 | 102 | this.setURL = function(newURL) { url = newURL; }; 103 | this.send = function(data){ 104 | if (transport) return transport.send(data); else return false; 105 | }; 106 | this.close = function(){ 107 | readyState = CLOSING; 108 | if (transport) transport.close(); 109 | }; 110 | }; 111 | 112 | return stream; 113 | } 114 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/js/n2o/facebook.js: -------------------------------------------------------------------------------- 1 | // {fb_id,"154227314626053"}, 2 | // {fb_secret,"cf9d49958ee536dd75f15bf8ca541965"}, 3 | 4 | utf8 = { toByteArray: utf8toByteArray }; 5 | 6 | window.fbAsyncInit = function() { 7 | 8 | FB.init({ appId: /* "559978657446014" */ "154227314626053", 9 | channelUrl: 'http://kakaranet.com/channel.html', 10 | status: true, cookie: true, xfbml: true, oauth: true }); 11 | 12 | // FB.getLoginStatus(function(response){ 13 | // if(response.status == 'connected' && fbLogin) 14 | // FB.api("/me?fields=id,username,gender,first_name,last_name,email,birthday", 15 | // function(response){ response.facebook = true; fbLogin(response);}); 16 | // }); 17 | 18 | }; 19 | 20 | function fb_login(){ 21 | FB.getLoginStatus(function(response){ 22 | console.log("Login Status: "+response.status); 23 | if(response.status == 'connected'){ 24 | if(fbLogin) 25 | FB.api("/me?fields=id,username,gender,first_name,last_name,email,birthday", 26 | function(response){fbLogin(response);}); 27 | } else FB.login(function(r){ 28 | if(r.authResponse && fbLogin) 29 | FB.api("/me?fields=id,gender,username,first_name,last_name,email,birthday", 30 | function(response){ fbLogin(response);}); 31 | }, {scope: 'email,user_birthday'}); 32 | }); 33 | } 34 | 35 | (function(d){ 36 | var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0]; 37 | if (d.getElementById(id)) {return;} 38 | js = d.createElement('script'); js.id = id; js.async = true; 39 | js.src = "//connect.facebook.net/en_US/all.js?v=2"; 40 | ref.parentNode.insertBefore(js, ref); 41 | }(document)); 42 | 43 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/js/n2o/kvs.js: -------------------------------------------------------------------------------- 1 | // Console Populate: 2 | // kvs_add({type:"user", id:"1",container:"feed",name:"maxim"}); 3 | // kvs_add({type:"user", id:"2",container:"feed",name:"maxim"}); 4 | // kvs_add({type:"user", id:"3",container:"feed",name:"maxim"}); 5 | 6 | // localStorage: 7 | // feeduser : {"type":"feed","id":"user","count":3,"top":"3"} 8 | // user1 : {"type":"user","id":"1","container":"feed","name":"maxim","next":"2","feed_id":"user"} 9 | // user2 : {"type":"user","id":"2","container":"feed","name":"maxim","prev":"1","next":"3","feed_id":"user"} 10 | // user3 : {"type":"user","id":"3","container":"feed","name":"maxim","prev":"2","next":null,"feed_id":"user"} 11 | 12 | // Console Traverse: 13 | // kvs_read("user","1",-1); 14 | // [Object,Object,Object] 15 | 16 | function kvs_put(o) { localStorage.setItem(o.type+o.id, JSON.stringify(o)); } 17 | 18 | function kvs_add(iterator) { 19 | var type = iterator.type; 20 | var id = iterator.id; 21 | var local = localStorage.getItem(type+id); 22 | if (null == local) { 23 | var container_name = iterator.container; 24 | var feed_id = iterator.feed_id ? iterator.feed_id : iterator.type; 25 | var container = localStorage.getItem(container_name+feed_id); 26 | if (null == container) { 27 | container = {type: iterator.container, id: feed_id, count: 0 }; 28 | kvs_put(container); 29 | } else container = JSON.parse(container); 30 | var prevTop = container.top; 31 | var prev = localStorage.getItem(type+prevTop); 32 | if (null != prev) { 33 | prev = JSON.parse(prev); 34 | prev.next = id; 35 | kvs_put(prev); 36 | iterator.prev = prev.id; 37 | } 38 | container.top = iterator.id; 39 | container.count++; 40 | iterator.next = null; 41 | iterator.feed_id = feed_id; 42 | kvs_put(container); 43 | kvs_put(iterator); 44 | } 45 | } 46 | 47 | function kvs_read(type,start,count) { return traversal(type,start,count,[]); } 48 | function traversal(type,start,count,result) { 49 | var item = localStorage.getItem(type+start); 50 | if (null == item || count == 0) return result; else { 51 | item = JSON.parse(item); 52 | result.push(item); 53 | if (count <= result.length && count != -1) return result; else 54 | return traversal(type,item.next,count,result); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/js/okey/card.js: -------------------------------------------------------------------------------- 1 | function CardScope(scope) { 2 | 3 | function Card(options) { 4 | options = options || {}, 5 | this.value = options.value, 6 | this.color = options.color, 7 | this.selected = !1, 8 | this.pos = {}, 9 | this.elements = { 10 | $circle: "circle", 11 | $text: ".value", 12 | $joker: ".joker", 13 | $overlay: ".overlay" 14 | }, 15 | this.proxies = [ "toggle", "selectGroup", "dragGroup", "clearGroup", "revertGroup" ], 16 | this.template(scope.CARD_SOURCE), 17 | this.$el.on("mousedown", this.toggle), 18 | this.$el.on("revert", this.revertGroup), 19 | this.$el.on("dragstop", this.clearGroup), 20 | this.$overlay.on("mousedown", this.selectGroup); 21 | } 22 | 23 | var selStart = !1; 24 | 25 | $(window).on("keydown", function(e) { e.altKey && (selStart = !0); }); 26 | $(window).on("keyup", function() { selStart = !1; }); 27 | 28 | Card.selected = []; 29 | Card.uncheckAll = function() { for (var i = Card.selected.length; i--; ) Card.selected[i].uncheck(); }; 30 | 31 | $("body").on("mousedown", Card.uncheckAll); 32 | 33 | $.inherit(Card, scope.Controller); 34 | 35 | $.extend(Card.prototype, { 36 | template: function(source) { 37 | $.load(source, this.proxy(this.load)); 38 | }, 39 | load: function(result) { 40 | this.$el = (this.$el || $("")).attr({ 41 | "class": "card" 42 | }).css({ 43 | cursor: "default" 44 | }).html(result), this.__super__.constructor.call(this), this.render(); 45 | }, 46 | render: function() { 47 | this.$joker.hide(); 48 | if (null == this.value && null == this.color) 49 | { 50 | this.$circle.hide(); 51 | this.$text.hide(); 52 | } else if (0 == this.value) { 53 | this.$circle.hide(); 54 | this.$text.show().attr({fill: this.color,"font-size":"26pt",y:42}).text("❦"); 55 | } else if (scope.gosterme != null && scope.gosterme.color == this.color && 56 | (scope.gosterme.value == this.value-1 || 57 | (scope.gosterme.value == 13 && this.value == 1)) && localStorage.getItem("showRealJoker") == "true") 58 | { 59 | this.$circle.hide(); 60 | this.$joker.show().attr({fill: this.color,"font-size":"20pt",y:52}).text("❦"); 61 | this.$text.show().attr("fill", this.color).text(this.value); 62 | } else { 63 | this.$circle.show().attr("fill", this.color); 64 | this.$text.show().attr("fill", this.color).text(this.value); 65 | }; 66 | }, 67 | drag: function() { 68 | this.dragHandler = new scope.Draggable(this.$el), this.dragHandler.owner = this; 69 | }, 70 | centerX: function() { 71 | var pos = this.$el.position(), width = this.$el.width(); 72 | return Math.round(pos.left + width / 2); 73 | }, 74 | 75 | centerY: function(){ 76 | var pos = this.$el.position(), height = this.$el.height(); 77 | return Math.round(pos.bottom + height / 2); 78 | }, 79 | 80 | toggle: function(e) { 81 | e.stopPropagation(), selStart && scope.deck.contains(this) ? this.nearSelectGroup() ? this.selected && !this.betweenSelected() ? this.uncheck() : this.check() : (Card.uncheckAll(), 82 | this.check()) : ~Card.selected.indexOf(this) || Card.uncheckAll(); 83 | }, 84 | check: function() { 85 | this.$overlay.css("display", ""), this.selected = !0, scope.deck.cards[this.pos.y][this.pos.x] = null, 86 | Card.selected.push(this); 87 | }, 88 | uncheck: function() { 89 | this.$overlay.css("display", "none"), this.selected = !1, scope.deck.cards[this.pos.y][this.pos.x] = this; 90 | var i; 91 | ~(i = Card.selected.indexOf(this)) && Card.selected.splice(i, 1); 92 | }, 93 | selectGroup: function(e) { 94 | if (!selStart) { 95 | for (var card, i = 0, l = Card.selected.length; l > i; i++) card = Card.selected[i], 96 | card != this && card.dragHandler.onDown(e, !0); 97 | this.$el.on("dragmove", this.dragGroup); 98 | } 99 | }, 100 | dragGroup: function(e) { 101 | for (var card, i = 0, l = Card.selected.length; l > i; i++) card = Card.selected[i], 102 | card != this && card.dragHandler.onMove(e.detail.event, !0); 103 | }, 104 | clearGroup: function() { 105 | this.$el.off("dragmove", this.dragGroup), selStart || Card.uncheckAll(); 106 | }, 107 | nearSelectGroup: function() { 108 | return Card.selected.some(function(card) { 109 | return Math.abs(card.pos.x - this.pos.x) <= 1 && card.pos.y == this.pos.y; 110 | }, this); 111 | }, 112 | betweenSelected: function() { 113 | Card.selected.sort(function(a, b) { 114 | return (a.pos.x < b.pos.x) - (b.pos.x < a.pos.x); 115 | }); 116 | var idx = Card.selected.indexOf(this); 117 | return -1 != idx && 0 != idx && idx != Card.selected.length - 1; 118 | }, 119 | revertGroup: function() { 120 | for (var card, i = 0, l = Card.selected.length; l > i; i++) card = Card.selected[i], 121 | card != this && card.$el.transform({ 122 | from: [ card.dragHandler.dx, card.dragHandler.dy ].join(" "), 123 | to: card.dragHandler.initTrf.join(" ") 124 | }), scope.deck.cards[card.pos.y][card.pos.x] = card; 125 | }, 126 | clone: function() { 127 | return new Card({ 128 | color: this.color, 129 | value: this.value 130 | }); 131 | }, 132 | tile: function(){ 133 | return [, scope.CARD_COLORS.indexOf(this.color), card.value] 134 | }, 135 | log: function() { 136 | console.log('%c'+this.value, 'color: '+this.color) 137 | } 138 | }); 139 | 140 | scope.Card = Card; 141 | } 142 | 143 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/js/okey/hand.js: -------------------------------------------------------------------------------- 1 | function HandScope(scope) { 2 | 3 | function Hand(root, options) 4 | { 5 | options = options || {}; 6 | this.$el = $(root); 7 | this.elements = { 8 | $topCard: "#top-card", 9 | $rollUp: "#roll-up", 10 | $rollDown: "#roll-down", 11 | $container: "#container", 12 | $back: "#back", 13 | $cards: "#cards" 14 | }; 15 | this.proxies = [ "show", "hide" ]; 16 | this.__super__.constructor.call(this); 17 | this.$rollUp.on("click", this.show); 18 | this.$rollDown.on("click", this.hide); 19 | this.cards = []; 20 | } 21 | 22 | var trfsMap = [ { backHeight: 74, containerTrf: "0 -44", cardTrf: "0 -38" }, 23 | { backHeight: 124, containerTrf: "0 -94", cardTrf: "0 -89" }, 24 | { backHeight: 174, containerTrf: "0 -144", cardTrf: "0 -139" } ]; 25 | 26 | var defCardTrf = "0 20"; 27 | 28 | $.inherit(Hand, scope.Controller); 29 | 30 | $.extend(Hand.prototype, { 31 | 32 | take: function() { 33 | var card = this.cards.pop(); 34 | return this.render(), card && card.$el.remove(), card; }, 35 | 36 | discard: function(tile) { 37 | var card = new scope.Card({ 38 | color: scope.CARD_COLORS[tile[1]-1], 39 | value: tile[2] 40 | }); 41 | this.cards.push(card), this.render(), card.drag(), card.dragHandler.disable(), this.$topCard.append(card.$el[0]); }, 42 | 43 | pop: function() { this.cards.pop(), this.render();}, 44 | 45 | show: function() { 46 | var count = this.cards.length - 1; 47 | count > 0 && (this.$container.transform({ 48 | to: trfsMap[4 > count ? count - 1 : 2].containerTrf, 49 | from: "0 50", 50 | dur: .355, 51 | calcMode: "linear" 52 | }), this.$back.animate({ 53 | attributeName: "height", 54 | to: trfsMap[4 > count ? count - 1 : 2].backHeight, 55 | dur: .355 56 | }), this.$cards.find("g").each(function(el, i) { 57 | 3 > i && $(el).transform({ 58 | to: trfsMap[i].cardTrf, 59 | from: "0 20", 60 | dur: .6, 61 | calcmode: "linear" 62 | }); 63 | }), this.shown = !0); }, 64 | 65 | hide: function() { 66 | var count = this.cards.length - 1; 67 | count > 0 && (this.$container.transform({ 68 | to: "0 50", 69 | from: trfsMap[4 > count ? count - 1 : 2].containerTrf, 70 | dur: .5, 71 | calcMode: "linear" 72 | }), this.$back.animate({ 73 | attributeName: "height", 74 | to: 0, 75 | dur: .5 76 | }), this.$cards.find("g").each(function(el, i) { 77 | 3 > i && $(el).transform({ 78 | to: defCardTrf, 79 | from: trfsMap[4 > count ? count - 1 : 2].cardTrf, 80 | dur: .6, 81 | calcmode: "linear" 82 | }); 83 | }), this.shown = !1); }, 84 | 85 | render: function() { 86 | var history = this.cards.slice(0, this.cards.length - 1); 87 | this.$cards.empty(), this.shown && this.hide(), history.reverse().forEach(function(card) { 88 | card = card.clone(), card.template(scope.CARD_SMALL_SOURCE), 89 | card.render(), 90 | (card.value == 0 && card.$text.attr({"font-size":"20pt",y:33})), 91 | card.$joker.attr({"font-size":"16pt",y:40,x:0}), 92 | card.$el.attr("transform", "translate(" + defCardTrf + ")"), 93 | this.$cards.append(card.$el[0]); 94 | }, this); }, 95 | 96 | clear: function() { 97 | this.cards.forEach(function(card) { 98 | card.$el.remove(); 99 | }), this.cards = [], this.render(); } 100 | 101 | }); 102 | 103 | scope.Hand = Hand; 104 | } 105 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/js/okey/okey_protocol.js: -------------------------------------------------------------------------------- 1 | 2 | function OkeyApiProviderScope(scope) { 3 | 4 | function ApiProvider(options) 5 | { 6 | options = options || {}; 7 | this.url = options.url; 8 | this.gameId = options.gameId; 9 | this.proxies = [ "init", "handleMessage", "actionTake" ]; 10 | this.proxyAll(); 11 | this.socket = new bullet(this.url); 12 | ws = this.socket; 13 | this.$socket = $(this.socket.transport()); 14 | this.socket.onopen = this.init; 15 | this.socket.ondisconnect = this.disconnect; 16 | this.socket.onmessage = this.handleMessage; 17 | } 18 | 19 | var eventMap = [ 20 | // general events 21 | "stats_event", 22 | // roster protocol 23 | "online_number", 24 | "online", 25 | "chat", 26 | "offline", 27 | "roster_item", 28 | "roster_group", 29 | "roster_end", 30 | "chat_message", 31 | "chat_event", 32 | // okey game protocol 33 | "logout", 34 | "okey_game_info", 35 | "okey_game_started", 36 | "okey_game_player_state", 37 | "okey_next_turn", 38 | "okey_enable", 39 | "wrong_reveal", 40 | "okey_tile_discarded", 41 | "okey_tile_taken", 42 | "okey_round_ended", 43 | "okey_revealed", 44 | "player_left", 45 | "game_paused" 46 | ]; 47 | 48 | $.extend(ApiProvider.prototype, { 49 | 50 | proxy: function(func) { 51 | return func.bind(this); 52 | }, 53 | proxyAll: function() { 54 | if (this.proxies) for (var method, i = this.proxies.length; i--; ) method = this.proxies[i], 55 | this[method] = this.proxy(this[method]); 56 | }, 57 | on: function(eventType, handler) { 58 | this.$socket.on(eventType, handler); 59 | }, 60 | off: function(eventType, handler) { 61 | this.$socket.off(eventType, handler); 62 | }, 63 | init: function() { 64 | if (!initialized) { this.socket.send([ "N2O", "" ]); initialized = true; } 65 | console.log("Connected"); 66 | appRun(); 67 | }, 68 | disconnect: function() { 69 | console.log("Disconnected"); 70 | for (var playerName in scope.playersMap) scope.playersMap[playerName].unselect(); 71 | scope.started = false; 72 | initialized = false; 73 | }, 74 | handleMessage: function(e) { 75 | var msg = JSON.parse(e.data); 76 | if (msg.eval) { try{eval(msg.eval); }catch(ex){console.log(ex);} } 77 | if (msg.data) { this.emitEvent(msg.data); } 78 | }, 79 | 80 | emitEvent: function(raw) { 81 | var msgName = dec(raw).value[0][0].value; 82 | if (msgName == "game_event") msgName = dec(raw).value[0][2].value; 83 | //console.log(String(dec(raw))); 84 | for (var event, i = eventMap.length, obj; i--; ) { 85 | event = eventMap[i]; 86 | if (eventMap[i] == msgName) 87 | this.$socket.trigger(msgName, {detail: {json:{},bert:raw} }); 88 | } 89 | 90 | }, 91 | actionTake: function(card) { 92 | var from = null != card.value ? 1 : 0; 93 | this.socket.send(enc(tuple(atom("client"), tuple(atom("game_action"), this.gameId, atom("okey_take"), { 94 | pile: from 95 | })))); 96 | }, 97 | actionDiscard: function(card) { 98 | this.socket.send(enc(tuple(atom("client"), tuple(atom("game_action"), this.gameId, atom("okey_discard"), { 99 | tile: tuple(atom("OkeyPiece"), scope.CARD_COLORS.indexOf(card.color) + 1, card.value) 100 | })))); 101 | }, 102 | reveal: function(card, hand) { 103 | this.socket.send(enc(tuple(atom("client"), tuple(atom("game_action"), this.gameId, atom("okey_reveal"), { 104 | discarded: tuple(atom("OkeyPiece"), scope.CARD_COLORS.indexOf(card.color) + 1, card.value), 105 | hand: hand 106 | })))); 107 | }, 108 | pause: function(resume) { 109 | this.socket.send(enc(tuple(atom("client"), 110 | tuple(atom("pause_game"), 111 | atom("undefined"), 112 | this.gameId, 113 | atom(resume ? "resume" : "pause"))))); 114 | } 115 | }), 116 | 117 | scope.ApiProvider = ApiProvider; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/js/player.js: -------------------------------------------------------------------------------- 1 | function PlayerScope(scope) { 2 | 3 | function Player(options) 4 | { 5 | options = options || {}, 6 | this.name = options.name; 7 | this.position = options.position; 8 | this.noSkin = options.noSkin; 9 | this.sex = options.sex; 10 | var skins = this.sex == "female" ? scope.FEMALE_SKINS : scope.MALE_SKINS; 11 | this.skin = options.skin || skins[$.rand(0, skins.length - 1)]; 12 | this.$el = $("#Player-" + this.position); 13 | this.elements = { 14 | $timer: "#Player-" + this.position + "-Timer", 15 | $name: "#Player-" + this.position + "-Display", 16 | $nameWrapper: "#Player-" + this.position + "-Display rect", 17 | $nameText: "#Player-" + this.position + "-Display text" }; 18 | this.proxies = [ "loadSkin" ]; 19 | this.__super__.constructor.call(this); 20 | this.$el.show(); 21 | this.$timer.hide(); 22 | this.$name.show(); 23 | 24 | setPlayerName("Player-" + this.position, this.name); 25 | 26 | this.initTimer(); 27 | 28 | "Me" == this.position || this.noSkin || $.load("svg/" + [ "Person", this.position, this.skin ].join("-") + ".svg", this.loadSkin); 29 | } 30 | 31 | $.inherit(Player, scope.Controller); 32 | 33 | $.extend(Player.prototype, { 34 | 35 | loadSkin: function(result) { 36 | 37 | var $result = $("").html(result); 38 | var element = $result[0].firstChild; 39 | /* var xform = parseTransformAttribute(element.getAttribute("transform")); 40 | var ori = parseTransformAttribute(this.$el[0].getAttribute("transform")); 41 | var shift = "translate("+(-parseFloat(ori.translate[0])+parseFloat(xform.translate[0]))+","+ 42 | (-parseFloat(ori.translate[1])+parseFloat(xform.translate[1]))+")"; 43 | element.setAttribute("transform",shift); 44 | */ this.$el.append(element); 45 | this.unselect(); }, 46 | 47 | initTimer: function() { 48 | this.timer = new scope.Timer(this.$timer, { duration: 30 }); }, 49 | 50 | select: function() { 51 | this.$nameWrapper.attr({ fill: "#517ECE" }), 52 | this.$nameText.attr({ fill: "#FFFFFF" }), 53 | this.$timer.show(), 54 | this.$("#Selection").show(), 55 | this.timer.start(); }, 56 | 57 | unselect: function() { 58 | this.$nameWrapper.attr({ fill: "#FFFFFF" }), 59 | this.$nameText.attr({ fill: "#48AF5E" }), 60 | this.$timer.hide(), 61 | this.$("#Selection").hide(), 62 | this.timer.reset(); } 63 | }); 64 | 65 | scope.Player = Player; 66 | } 67 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/js/roster.js: -------------------------------------------------------------------------------- 1 | 2 | function RosterScope(scope) 3 | { 4 | function Roster(scope) { RosterHandlers(scope); } 5 | scope.Roster = Roster; 6 | } 7 | 8 | function RosterHandlers(scope) { 9 | 10 | scope.apiProvider.on("online_number", function(x) { 11 | var e = {detail: x.detail.json, raw: x.detail.bert}; 12 | var number = dec(e.raw).value[0][1]; 13 | document.getElementById("Users-Online-Number").firstElementChild.textContent = number; 14 | }); 15 | 16 | scope.apiProvider.on("online", function (x) { 17 | var e = {detail: x.detail.json, raw: x.detail.bert}; 18 | var id = dec(e.raw).value[0][1].value; 19 | name = dec(e.raw).value[0][2].value; 20 | surname = dec(e.raw).value[0][3].value; 21 | score = dec(e.raw).value[0][4].value; 22 | if (null != document.getElementById(id.entag())) removeOnlineUser(id); 23 | addOnlineUser(id,name+" "+surname,score,"insertTop"); 24 | if ( name == document.names) 25 | $("#Quota")[0].lastElementChild.textContent = i18n("Score") + ": " + score; 26 | if (currentChat == null) showOnlineList(); 27 | }); 28 | 29 | scope.apiProvider.on("offline", function (x) { 30 | var e = {detail: x.detail.json, raw: x.detail.bert}; 31 | var id = dec(e.raw).value[0][1].value; 32 | name = dec(e.raw).value[0][2].value; 33 | surname = dec(e.raw).value[0][3].value; 34 | score = dec(e.raw).value[0][4].value; 35 | if (null != document.getElementById(id.entag())) removeOnlineUser(id); 36 | addOnlineUser(id,name+" "+surname,score,"appendChild"); 37 | }); 38 | 39 | scope.apiProvider.on("roster_item", function (x) { 40 | var e = {detail: x.detail.json, raw: x.detail.bert}; 41 | var id = dec(e.raw).value[0][1].value; 42 | name = dec(e.raw).value[0][2].value; 43 | surname = dec(e.raw).value[0][3].value; 44 | score = dec(e.raw).value[0][4].value; 45 | addOnlineUser(id,name+" "+surname,score,"appendChild"); 46 | }); 47 | 48 | scope.apiProvider.on("roster_end", function (x) { 49 | var e = {detail: x.detail.json, raw: x.detail.bert}; 50 | if (currentChat == null) showOnlineList(); 51 | var now = new Date().getTime(); 52 | var page_load_time = now - perfCounter.start; 53 | console.log(user_count + " users loaded in " + page_load_time + "ms"); 54 | }); 55 | 56 | scope.apiProvider.on("chat_message", function (x) { 57 | var e = {detail: x.detail.json, raw: x.detail.bert}; 58 | var from = dec(e.raw).value[0][1].value[0][0].value, 59 | names = dec(e.raw).value[0][1].value[0][1].value, 60 | to = dec(e.raw).value[0][2].value, 61 | message = dec(e.raw).value[0][3].value; 62 | chatMessage("Chat+"+from.entag(),"1",from==document.user?"Self":from,utf8decode(message)); 63 | if (null != currentChat) { 64 | onlineHover(); 65 | mouseWheelHandler({'detail':-10000,'wheelDelta':-10000}); 66 | onlineHoverOut(); 67 | } 68 | }); 69 | 70 | scope.apiProvider.on("chat_event", function(x) { 71 | var e = {detail: x.detail.json, raw: x.detail.bert}; 72 | var gameId = dec(e.raw).value[0][1]; 73 | var userId = dec(e.raw).value[0][2].value; 74 | var name = dec(e.raw).value[0][3]; 75 | var message = dec(e.raw).value[0][4]; 76 | if (userId != document.user) 77 | { 78 | chatMessage("Chat","1",userId==document.user?"Self":userId,utf8decode(name)+":\n"+utf8decode(message)); 79 | scroll_right = -10000; 80 | barHover(); 81 | mouseWheelHandler({'detail':-10000,'wheelDelta':-10000}); 82 | barHoverOut(); 83 | } 84 | }); 85 | 86 | scope.apiProvider.on("stats_event", function (x) { 87 | var e = {detail: x.detail.json, raw: x.detail.bert}; 88 | displayPlayerInfo(function() { 89 | document.getElementById('Player-Statistics').style.display = ''; 90 | var games = dec(e.raw).value[0][2], 91 | reveals = dec(e.raw).value[0][3], 92 | protocol = dec(e.raw).value[0][4]; 93 | score = dec(e.raw).value[0][5].value; 94 | removeChilds(document.getElementById('Stat-Left')); 95 | removeChilds(document.getElementById('Stat-Right')); 96 | 97 | // for (var i=0,iter=0;i 0 && (this.$value.text(this.curTime), 44 | this.timerId = this.withDelay(this.tick, 1e3)) 45 | ); 46 | }, 47 | reset: function() { 48 | this.$progress.stop().attr({ 49 | height: defHeight 50 | }), 51 | this.$value.text(this.curTime = this.duration), 52 | 53 | clearTimeout(this.timerId); 54 | }, 55 | pause: function() { 56 | this.paused = !0, this.$progress.pause(); 57 | }, 58 | resume: function() { 59 | this.paused = !1, this.$progress.resume(); 60 | } 61 | }); 62 | 63 | scope.Timer = Timer; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/js/translations.js: -------------------------------------------------------------------------------- 1 | locale = { en: {}, tr: {} }; 2 | 3 | // Buttons and Interface 4 | 5 | locale.tr.Score = "Puan"; 6 | locale.tr.Online = "Oynayanlar"; 7 | locale.tr.Games = "Oyunlar"; 8 | locale.tr.Reveals = "Bitirdi"; 9 | locale.tr.Rules = "Kurallar"; 10 | locale.tr.Quota = "Kota"; 11 | locale.tr.Kakush = "Kakuş"; 12 | locale.tr.Login = "İle bağlan"; 13 | locale.tr.Statistics = "İstatistikler"; 14 | 15 | // Chats 16 | 17 | locale.en.EditMessage = "Write some text here"; 18 | locale.tr.EditMessage = "Chat için buraya yazabilirsiniz"; 19 | locale.en.GameChat = "This is In-Game chat"; 20 | locale.tr.GameChat = "Oyun içi sohbet"; 21 | locale.en.PrivateChat = " is waiting for chat with you"; 22 | locale.tr.PrivateChat = " seninle sohbet için bekliyor"; 23 | 24 | // Messages 25 | 26 | locale.tr.Paused = "ara istiyor"; 27 | locale.en.Paused = "paused"; 28 | locale.tr.Revealed = "bitirdi"; 29 | locale.en.Revealed = "revealed"; 30 | locale.en.WrongReveal = "Wrong Reveal. Try next time."; 31 | locale.tr.WrongReveal = "Bu el yanlış. Bitirde gel"; 32 | 33 | // Rules Dialog 34 | 35 | locale.en.FakeJoker = "Fake Joker"; 36 | locale.tr.FakeJoker = "Sahte okey"; 37 | locale.tr.WinningDeck = "Açan el"; 38 | locale.en.WinningDeck = "Winning Deck"; 39 | locale.tr.OkeyRules = "Oyun kuralları"; 40 | locale.en.OkeyRules = "Okey Rules"; 41 | locale.tr.Run = "Sıralı"; 42 | locale.tr.Set = "Asker"; 43 | locale.tr.Pair = "Çift"; 44 | 45 | // Protocol 46 | 47 | locale.tr.player_left = "Oyundan kaçış"; 48 | locale.tr.okey_revealed = "Açtığı el"; 49 | locale.tr.okey_round_ended = "Tamamladığı oyun"; 50 | locale.tr.okey_turn_timeout = "Zamanında oynamama"; 51 | locale.tr.wrong_reveal = "Yanlış açma"; 52 | locale.tr.okey_game_started = "Oynadığı oyun"; 53 | 54 | function i18n(word) 55 | { 56 | var translation = locale[currentLanguage()][word]; 57 | return (translation == null) ? word : translation; 58 | } 59 | 60 | function currentLanguage() 61 | { 62 | var currentLocale = localStorage.getItem("locale"); 63 | return currentLocale == null ? "en" : currentLocale; 64 | } 65 | 66 | function switchLanguage() 67 | { 68 | if (localStorage.getItem("locale") == "tr") 69 | { 70 | $("#Flag-tr").hide(); 71 | $("#Flag-en").show(); 72 | localStorage.setItem("locale","en"); 73 | } else { 74 | $("#Flag-en").hide(); 75 | $("#Flag-tr").show(); 76 | localStorage.setItem("locale","tr"); 77 | } 78 | } 79 | 80 | function translateScene(e) 81 | { 82 | if (document.getElementById("Okey-Rules") != null) { 83 | $("#Okey-Rules-Text")[0].lastElementChild.textContent = i18n("OkeyRules"); 84 | $("#Pair")[0].lastElementChild.textContent = i18n("Pair"); 85 | $("#Set")[0].lastElementChild.textContent = i18n("Set"); 86 | $("#Fake-Joker")[0].lastElementChild.textContent = i18n("FakeJoker"); 87 | $("#Run")[0].lastElementChild.textContent = i18n("Run"); 88 | $("#Winning-Deck")[0].lastElementChild.textContent = i18n("WinningDeck"); 89 | if (currentLanguage() == "en") { $("#Turkish-Rules").hide(); $("#English-Rules").show(); } 90 | else { $("#Turkish-Rules").show(); $("#English-Rules").hide(); } 91 | } 92 | 93 | if (document.getElementById("Player-Statistics") != null) { 94 | $("#Games-Text")[0].lastElementChild.textContent = i18n("Games"); 95 | $("#Reveals-Text")[0].lastElementChild.textContent = i18n("Reveals"); 96 | if (currentLanguage() == "en") { $("#Turkish-StatNotes").hide(); $("#English-StatNotes").show(); } 97 | else { $("#Turkish-StatNotes").show(); $("#English-StatNotes").hide(); } 98 | } 99 | 100 | try { 101 | document.getElementById("GameChatEditor").firstElementChild.textContent = i18n("EditMessage"); 102 | document.getElementById("OnlineChatEditor").firstElementChild.textContent = i18n("EditMessage"); 103 | } catch (e) { console.log("Please add foreignObjects to schene from SVG.txt"); } 104 | 105 | 106 | 107 | $("#Users-Online-Message")[0].firstElementChild.textContent = i18n("Online"); 108 | $("#Point-Table").find("text")[0].lastElementChild.textContent = i18n("Statistics"); 109 | $("#Rules").find("text")[0].lastElementChild.textContent = i18n("Rules"); 110 | $("#Kakush")[0].lastElementChild.textContent = i18n("Kakush") + ": " + 0; 111 | $("#Gabrielo-Discard-Shape").hide(); 112 | $("#Center-Card-Selection").hide(); 113 | $("#You-Discard-Shape").hide(); 114 | 115 | $('#Facebook-Login').on('click',function(x) { fb_login(); }); 116 | $('#Facebook-Login').attr({cursor:'pointer'}); 117 | $('#Login-Text')[0].style.cursor = 'pointer'; 118 | // $("#Login-Text")[0].lastElementChild.textContent = i18n("Login"); 119 | $("#Login-Text").text(i18n("Login")).attr({x:16,y:23}); 120 | 121 | // $("#Okey").hide(); 122 | $("#Okey").on("click", sendSawOkey); 123 | // $("#Have-8-Tashes").hide(); 124 | 125 | } 126 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/mp3/ding.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devaspot/games/a1f7c3169c53d31e56049e90e0094a3f309603ae/apps/web/priv/static/app/mp3/ding.mp3 -------------------------------------------------------------------------------- /apps/web/priv/static/app/svg/Card-Small.svg: -------------------------------------------------------------------------------- 1 | 2 | a 3 | 4 | b 5 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/svg/Card.svg: -------------------------------------------------------------------------------- 1 | 2 | x 3 | y 4 | 5 | 6 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/svg/Deck.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/svg/Discarder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/svg/Person-Center-Alina.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/svg/Person-Left-Alina.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/svg/Person-Left-Mustafa.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/svg/Person-Right-Alina.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /apps/web/priv/static/app/svg/Player-Statistics.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Player-Statistics 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | How good is Player (win/lose) per each game type, and how 18 | player usualy reveals the games. 19 | 20 | 21 | OK 22 | 23 | 24 | Lucky 25 | Std Color 124/23 26 | Std Even/Odd 234/2 27 | Std Countdown 20/10 28 | Tour Color 512 30/100 29 | Tour Even/Odd 64 44/23 30 | 31 | 32 | Have 8 Tashes 40 33 | Normal 124 34 | Okey 234 35 | 36 | 37 | Reveals 38 | 39 | 40 | Games 41 | 42 | 43 | Score: 722 44 | 45 | 46 | Rank: 22 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/Kakaranet-Scene.sketch/Data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devaspot/games/a1f7c3169c53d31e56049e90e0094a3f309603ae/apps/web/priv/static/doc/Kakaranet-Scene.sketch/Data -------------------------------------------------------------------------------- /apps/web/priv/static/doc/Kakaranet-Scene.sketch/metadata: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | app 6 | com.bohemiancoding.sketch3 7 | build 8 | 8054 9 | commit 10 | b2079fe10151ad0eef6cc553b7369ec78d67b9b5 11 | fonts 12 | 13 | .LucidaGrandeUI 14 | LucidaGrande-Bold 15 | ZapfDingbatsITC 16 | 17 | length 18 | 3020774 19 | version 20 | 37 21 | 22 | 23 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/Kakaranet-Scene.sketch/version: -------------------------------------------------------------------------------- 1 | 37 -------------------------------------------------------------------------------- /apps/web/priv/static/doc/bullet.js: -------------------------------------------------------------------------------- 1 | function bullet(url) { 2 | 3 | var CONNECTING = 0; 4 | var OPEN = 1; 5 | var CLOSING = 2; 6 | var CLOSED = 3; 7 | 8 | var transports = { 9 | 10 | websocket: function() { 11 | var transport = null; 12 | if (window.WebSocket) { transport = window.WebSocket; } 13 | if (window.MozWebSocket) { transport = window.MozWebSocket; } 14 | if (transport) { return {'heart': true, 'transport': transport}; } 15 | return null; 16 | }, 17 | 18 | xhrPolling: function() { 19 | var timeout; 20 | var xhr; 21 | 22 | nextPoll(); 23 | 24 | var fake = { 25 | readyState: CONNECTING, 26 | 27 | receive: function(data) { 28 | if (fake.readyState == CONNECTING) { fake.readyState = OPEN; fake.onopen(fake); } 29 | if (data.length != 0) { fake.onmessage({'data': data }); } 30 | if (fake.readyState == OPEN) { nextPoll(); } 31 | }, 32 | 33 | send: function(data) { 34 | if (this.readyState != CONNECTING && this.readyState != OPEN) return false; 35 | var fakeurl = url.replace('ws:', 'http:').replace('wss:', 'https:'); 36 | var request = new XMLHttpRequest(); 37 | request.open('POST',fakeurl,true); 38 | request.setRequestHeader('Content-Type', 39 | 'application/x-www-form-urlencoded; charset=utf-8'); 40 | request.setRequestHeader('X-Socket-Transport','xhrPolling'); 41 | request.onload = function() { fake.receive(request.response); } 42 | request.send(data); 43 | return true; 44 | }, 45 | close: function(){ 46 | this.readyState = CLOSED; 47 | xhr.abort(); 48 | clearTimeout(timeout); 49 | fake.onclose(); 50 | }, 51 | onopen: function(){}, 52 | onmessage: function(){}, 53 | onerror: function(){}, 54 | onclose: function(){} 55 | }; 56 | 57 | function poll(pooling){ 58 | var fakeurl = url.replace('ws:', 'http:').replace('wss:', 'https:'); 59 | var request = new XMLHttpRequest(); 60 | request.open('GET',fakeurl,true); 61 | request.setRequestHeader('Content-Type', 62 | 'application/x-www-form-urlencoded; charset=utf-8'); 63 | request.setRequestHeader('X-Socket-Transport','xhrPolling'); 64 | request.onload = function() { fake.receive(request.response); } 65 | request.onerror = function() { fake.onerror(); } 66 | request.send({}); 67 | } 68 | 69 | function nextPoll() { timeout = setTimeout(function(){poll();}, 1000); } 70 | 71 | 72 | return {'heart': false, 'transport': function(){ return fake; fake.nextPoll(); }}; 73 | } 74 | }; 75 | 76 | var tn = 0; 77 | function next() { 78 | var c = 0; 79 | 80 | for (var f in transports) { 81 | if (tn == c) { 82 | var t = transports[f](); 83 | if (t) { var ret = new t.transport(url); ret.heart = t.heart; return ret; } 84 | tn++; 85 | } 86 | c++; 87 | } 88 | return false; 89 | } 90 | 91 | var stream = new function() { 92 | var isClosed = true; 93 | var readyState = CLOSED; 94 | var heartbeat; 95 | var delay = 80; 96 | var delayDefault = 80; 97 | var delayMax = 10000; 98 | 99 | var transport; 100 | function init() { 101 | 102 | isClosed = false; 103 | readyState = CONNECTING; 104 | transport = next(); 105 | 106 | if (!transport) { 107 | delay = delayDefault; 108 | tn = 0; 109 | stream.ondisconnect(); 110 | setTimeout(function(){init();}, delayMax); 111 | return false; 112 | } 113 | 114 | transport.onopen = function() { 115 | delay = delayDefault; 116 | 117 | if (transport.heart) { 118 | heartbeat = setInterval(function(){stream.onheartbeat();}, 4000); 119 | } 120 | 121 | if (readyState != OPEN) { readyState = OPEN; stream.onopen(); } 122 | }; 123 | 124 | transport.onclose = function() { 125 | if (isClosed) { return; } 126 | 127 | transport = null; 128 | clearInterval(heartbeat); 129 | 130 | if (readyState == CLOSING){ 131 | readyState = CLOSED; 132 | stream.onclose(); 133 | } else { 134 | if (readyState == CONNECTING) tn++; 135 | delay *= 2; 136 | if (delay > delayMax) { delay = delayMax; } 137 | isClosed = true; 138 | setTimeout(function() { init(); }, delay); 139 | } 140 | }; 141 | transport.onerror = transport.onclose; 142 | transport.onmessage = function(e){ 143 | stream.onmessage(e); 144 | }; 145 | } 146 | 147 | init(); 148 | 149 | this.onopen = function(){}; this.oninit = function(){}; 150 | this.onmessage = function(){}; this.ondisconnect = function(){ initialized = false; }; 151 | this.onclose = function(){}; this.onheartbeat = function(){ return this.send('PING'); }; 152 | 153 | this.setURL = function(newURL) { url = newURL; }; 154 | this.send = function(data){ 155 | if (transport) return transport.send(data); else return false; 156 | }; 157 | this.close = function(){ 158 | readyState = CLOSING; 159 | if (transport) transport.close(); 160 | }; 161 | }; 162 | 163 | return stream; 164 | } 165 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/contents.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

Documents

9 | Client 10 |
11 | N2O Protocol
12 | Game Protocol
13 | SVG Mock
14 | Campaigns SVG Mock
15 | SVG App Requirements
16 | Drag Spec
17 |
18 | 19 | Server
20 |
21 | Game Server
22 |
23 | 24 | Schedule
25 |
26 | April 2014
27 | May 2014
28 | June 2014
29 | July 2014
30 |
31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/drag.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 |
11 |
12 |
13 | 14 |

Card Dragging

15 | 16 |

1. Устойчивые Карты

17 | 18 |

Когда мы ведем карту над доской, она встряхивает другие карты, 19 | но как только ведомая карта уходит за пределы группы, встряхнутые 20 | карты возвращаются на свое место. Мы изменяем позицию карт на 21 | доске только когда отжали кнопку. Текущее поведение -- стик при 22 | таймауте -- тоже неверно, карта должна возвращаться на свое место.

23 | 24 | 25 | 26 |

2. Зона захвата карты

27 | 28 |

Сейчас зона захвата определяется по курсору. Это не совсем удобно. 29 | Напрмер для зоны сбрасывания достаточно что бы 25% накрывало зону сбрасываныя. 30 | То же касается и случая когда карта находится посредине между четырех зон на доске.

31 | 32 | 33 | 34 | 35 |

3. Карты не могут накрывать друг друга

36 | 37 |

Карты не могут накрывать друг друга. Это ошибочное поведение.

38 | 39 | 40 |

4. Карта должна легко ставиться между картами

41 | 42 |

Сейчас, чтобы поставить карту между двумя картами нужно 43 | сделать минимум два движения. Так быть не должно. Не надо так.

44 | 45 |

5. Двигать группы вправо и влево

46 | 47 |

Мы должны иметь возможность двигать группу картой в направление движения. 48 | Наприме если мы хохим подвинуть группу влево (вправо), то мы должны 49 | справа (слева) коснуться ее ведомой картой. Неважно откуда эта карта взялась.

50 | 51 | 52 | 53 | 54 |
55 | 56 |
57 | 58 |
59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/game_server.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 |
11 |
12 |
13 | 14 |

Game Server Architecture

15 | 16 |

Bots and Players

17 | 18 |

Bots are special gen_server that acts similar to 19 | connection handlers. They also owns game_session instance 20 | and loop bot logic for games. Same the conn_handler acts 21 | just like a bots it is relying on network connection 22 | for user actions and also own game_session.

23 | 24 |

User Session

25 | 26 |

Session gen_server for every logged user that 27 | holds the references to game relays and chat relays.

28 | 29 |

Game FSMs

30 | 31 |

Game events in gen_fsm module for particular games 32 | implementations that hold the board information, 33 | link to game relay and users sessions pids.

34 | 35 |

Relays

36 | 37 |

Relay for routing messages from/to user session 38 | instances and/or clients within game instances that 39 | hold link to game module.

40 | 41 |

This picture describes the structural organization of 42 | modules and run-time connections of instances. As you 43 | can see Game Relay is tightly coupled with particular 44 | board implementation and Session connected with Bot 45 | implementations and Connection Handler (for real users). 46 | 47 | All four blocks are gen_servers and just board game implementation is gen_fsm.

48 | 49 | 50 | +---------------+ 51 | | Game Manager | 52 | +---------------+ 53 | | TOURNAMENT | 54 | +---------------+ 55 | | 56 | V 57 | +---------------+ 58 | | RELAY | 59 | +---------------+ 60 | | GAME FSM | 61 | +---------------+ 62 | | | 63 | +----------------+ +----------------+ 64 | | USER SESSION | | USER SESSION | 65 | +----------------+ +----------------+ 66 | | BOT | | PLAYER | 67 | +----------------+ +----------------+ 68 | 69 | 70 |

Bots and Players

71 | 72 |

Each instance of game_session holds the state of each player in system. 73 | Once created by connection handler or by bot modules game session responds to 74 | requests from game clients (bots and players).

75 | 76 |

send_message { }

77 | 78 | Sends async message down to the bot logic or directly to connection socket. 79 | 80 |

call_rpc { }

81 | 82 | Process the request to underlying game_session. 83 | 84 |

get_session { }

85 | 86 | Return underlying game_session. 87 | 88 |

init_state { }

89 | 90 | Override 91 | 92 |

join_game { }

93 | 94 | Create 95 | 96 |

User Session

97 | 98 |

state { user,rpc,rpc_mon,games,chats,match_request }

99 | 100 |

The state hold user info, rpc for pid of self session instance 101 | and its erlang:monitor. Also holds all games 102 | participations (relay pids) and chats relays for given user.

103 | 104 |

session_attach { token }

105 | 106 | Used for authenticate the client by the registered 107 | token by the authentication server. 108 | If the user is not found, then terminate send to client pid. 109 | Also you can check for session_attach_debug for 110 | authentication by token and username. 111 | 112 | It replaces the user in state of game_session from 113 | unknown to authorized user info #'PlayerInfo'. 114 | This is the first message sent by Flex client. 115 | Reply with #'PlayerInfo', if not found invalid_token returns. 116 | 117 |

logout { }

118 | 119 | Send termination to client pid uncoditionally. Please use this function for correct consumer behavior at the end of gaming session. Reply is ok. 120 | 121 |

chat { chat_id, message }

122 | 123 | Used for talking through publishing messages to relay. 124 | chat_id is actually the game id to which message should be relayed. 125 | Result not valuable, in case of failure returns chat_not_registered. 126 | 127 |

get_player_info { player_id = 0 }

128 | 129 | Reply with current #'PlayerInfo' stored in State. Other player_id 130 | parameters replies with not_implemented. 131 | 132 | 133 |

match_me { game_type }

134 | 135 | Asynchronous match the current user in state along with game type. 136 | Replies with generated request ref integer that will be answered 137 | in match_found API. 138 | 139 |

match_found { ref,game_id,is_replacing,pid }

140 | 141 | Answer to matching request ref with relay as pid, and running game_id. 142 | 143 |

join_game { game }

144 | 145 | Join current user in state for a particular game instance by its id. 146 | This is the second message send by Flex client. Returns #'TableInfo' for success. In case of failure returns: game_not_found, already_joined, this_game_is_private. 147 | 148 |

game_event { game, event, args }

149 | 150 | This is both info and cast async API for handling game events. 151 | Send async notification message from SERVER to PLAYER. 152 | 153 |

game_action { game, action, msg }

154 | 155 | This is just like game_event but being sent from PLAYERS to SERVER. 156 | 157 |

pause_game { game, action }

158 | 159 | Action could be pause or resume. Then message downstreams to FSM by signaling. 160 | 161 |

rematch { game }

162 | 163 | Rematching request for current player in USER SESSION for existing game instance. Message forwarded to FSM module by signaling. 164 | 165 |

game_crashed { game }

166 | 167 |

game_rematched { game }

168 | 169 |

chat_msg { game }

170 | 171 |

social_action { game, type, initiator, recepient }

172 | 173 |

social_action_msg { type, initiator, recepient }

174 | 175 | 176 | 177 |
178 | 179 |
180 | 181 |
182 | 183 |
184 | 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/images/drag1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devaspot/games/a1f7c3169c53d31e56049e90e0094a3f309603ae/apps/web/priv/static/doc/images/drag1.png -------------------------------------------------------------------------------- /apps/web/priv/static/doc/images/drag2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devaspot/games/a1f7c3169c53d31e56049e90e0094a3f309603ae/apps/web/priv/static/doc/images/drag2.png -------------------------------------------------------------------------------- /apps/web/priv/static/doc/images/drag3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devaspot/games/a1f7c3169c53d31e56049e90e0094a3f309603ae/apps/web/priv/static/doc/images/drag3.png -------------------------------------------------------------------------------- /apps/web/priv/static/doc/images/drag4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devaspot/games/a1f7c3169c53d31e56049e90e0094a3f309603ae/apps/web/priv/static/doc/images/drag4.png -------------------------------------------------------------------------------- /apps/web/priv/static/doc/images/drag5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devaspot/games/a1f7c3169c53d31e56049e90e0094a3f309603ae/apps/web/priv/static/doc/images/drag5.png -------------------------------------------------------------------------------- /apps/web/priv/static/doc/kvs.js: -------------------------------------------------------------------------------- 1 | // Console Populate: 2 | // kvs_add({type:"user", id:"1",container:"feed",name:"maxim"}); 3 | // kvs_add({type:"user", id:"2",container:"feed",name:"maxim"}); 4 | // kvs_add({type:"user", id:"3",container:"feed",name:"maxim"}); 5 | 6 | // localStorage: 7 | // feeduser : {"type":"feed","id":"user","count":3,"top":"3"} 8 | // user1 : {"type":"user","id":"1","container":"feed","name":"maxim","next":"2","feed_id":"user"} 9 | // user2 : {"type":"user","id":"2","container":"feed","name":"maxim","prev":"1","next":"3","feed_id":"user"} 10 | // user3 : {"type":"user","id":"3","container":"feed","name":"maxim","prev":"2","next":null,"feed_id":"user"} 11 | 12 | // Console Traverse: 13 | // kvs_read("user","1",-1); 14 | // [Object,Object,Object] 15 | 16 | function kvs_put(o) { localStorage.setItem(o.type+o.id, JSON.stringify(o)); } 17 | 18 | function kvs_add(iterator) { 19 | var type = iterator.type; 20 | var id = iterator.id; 21 | var local = localStorage.getItem(type+id); 22 | if (null == local) { 23 | var container_name = iterator.container; 24 | var feed_id = iterator.feed_id ? iterator.feed_id : iterator.type; 25 | var container = localStorage.getItem(container_name+feed_id); 26 | if (null == container) { 27 | container = {type: iterator.container, id: feed_id, count: 0 }; 28 | kvs_put(container); 29 | } else container = JSON.parse(container); 30 | var prevTop = container.top; 31 | var prev = localStorage.getItem(type+prevTop); 32 | if (null != prev) { 33 | prev = JSON.parse(prev); 34 | prev.next = id; 35 | kvs_put(prev); 36 | iterator.prev = prev.id; 37 | } 38 | container.top = iterator.id; 39 | container.count++; 40 | iterator.next = null; 41 | iterator.feed_id = feed_id; 42 | kvs_put(container); 43 | kvs_put(iterator); 44 | } 45 | } 46 | 47 | function kvs_read(type,start,count) { return traversal(type,start,count,[]); } 48 | function traversal(type,start,count,result) { 49 | var item = localStorage.getItem(type+start); 50 | if (null == item || count == 0) return result; else { 51 | item = JSON.parse(item); 52 | result.push(item); 53 | if (count <= result.length && count != -1) return result; else 54 | return traversal(type,item.next,count,result); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/n2o_proto.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 |
11 |
12 |
13 | 14 |

N2O Protocol

15 | 16 |

Requests

17 | 18 |

pickle

19 | 20 | Picled messaged are used if you send messages over unencrypted 21 | channel and want to hide the content of the message, 22 | that was generated on server. You can use BASE64 pickling mechanisms 23 | with optional AES/RIPEMD160 encrypting. 24 | 25 | ws.send(enc( 26 | tuple(atom('pickle'), 27 | binary('ddtake'), 28 | binary('g2gCaAVkAAJldmQABWluZGV4ZAAEdGFrZWsABH'+ 29 | Rha2VkAAVldmVudGgDYgAABXViAAQKXmIAC3cK'), 30 | [tuple(atom('ddtake'),'0')]))); 31 | 32 | Where Base64 represents the N2O EVENT: 33 | 34 | #ev{module=index,payload=take,trigger="take",name=event} 35 | 36 |

This is Nitrogen-based messaging model.
37 | This request will return JSON with EVAL field only.

38 | 39 |

client

40 | 41 |

Client messages usually originated at client and represent the Client API Requests:

42 | 43 | ws.send(enc( 44 | tuple( 45 | atom('client'), 46 | tuple(atom('join_game'),1000001)))); 47 | 48 |

NOTE: This request may return JSON with EVAL and DATA fields.

49 | 50 |

bert

51 | 52 |

When you want to receive BERT messages on client you usually ask the 53 | server by sending:

54 | 55 | ws.send(enc( 56 | tuple( 57 | atom('bert'), 58 | binary('API Request')); 59 | 60 |

This messages could be handled as this:

61 | 62 | event({bert,Message}) -> 63 | wf:info("This API will return BERT enveloped echo"), 64 | {answer,echo,Message}; 65 | 66 |

NOTE: you will always receive BERT messages.

67 | 68 |

binary

69 | 70 |

When you need raw binary Blob on client side, 71 | for images or other raw data you can ask server like this:

72 | 73 | ws.send(enc( 74 | tuple( 75 | atom('binary'), 76 | binary('API Request')); 77 | 78 |

And handle also in binary clause:

79 | 80 | event({binary,Message}) -> 81 | wf:info("This API will return Raw Binary"), 82 | <<84,0,0,0,108>>; 83 | 84 |

NOTE: if event returns not the binary client will recieve BERT encoded message.

85 | 86 |

server

87 | 88 | Server messages are usually being sent to client originated on the 89 | server by sending info notifications directly to Web Socket process: 90 | 91 | > WebSocketPid ! {server, Message} 92 | 93 |

You can obtain this Pid like here:

94 | 95 | event(init) -> wf:info("Web Socket Pid: ~p",[self()]); 96 | 97 |

You can also send server messages from client relays:

98 | 99 | ws.send(enc( 100 | tuple( 101 | atom('server'), 102 | binary('Binary Message')); 103 | 104 | 105 |

NOTE: This request may return JSON with EVAL and DATA fields.

106 | 107 |

Responses

108 | 109 |

JSON envelop

110 | 111 |

Each message from Web Socket channel to Client encoded as JSON object. 112 | N2O.js 113 | is used to decode WebSocket binary messages from JSON container.

114 | 115 | { "eval": "ws.send("Send Back This String");", 116 | "data": [131,104,2,100,0,7,109,101,115,115, 117 | 97,103,101,107,0,5,72,101,108,108,111] } 118 | 119 | 120 |

EVAL values evaluated immediately and DATA values passed 121 | to handle_web_socket(data) function if exists.

122 | 123 | function handle_web_socket(body) { console.log(body); } 124 | 125 |

JSON enveloped BERT

126 | 127 |

Usually in DATA come BERT messages (Binary Erlang Term Format). 128 | js 129 | is used to decode Game Protocol message.

130 | 131 | function handle_web_socket(body) { 132 | console.log(String(dec(body))); } 133 | 134 | E> Received: {message,"Hello"} 135 | 136 |

BERT binary

137 | 138 | function handle_web_socket(body) { 139 | console.log(String(dec(body))); } 140 | 141 |

RAW binary

142 | 143 | function handle_web_socket_blob(body) { } 144 | 145 | E> Unknown Raw Binary Received: [72,101,108,108,111] 146 | 147 |
148 | 149 |
150 | 151 |
152 | 153 |
154 | 155 | 156 | 157 | 158 | 159 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/plans/april2014.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 |
11 |
12 |
13 | 14 |

Kakaranet April 2014

15 | 16 |

1. Bootstrap kakaranet/games

17 |

1. Create SVG Mock

18 |

2. Refactor Game Server

19 |

2. Make Debug Playable Okey in browser

20 |

1. Adopt KVS database

21 |

2. Write JavaScript Library

22 |

3. Create n2o_game Endpoint for Game

23 |

4. Remove jQuery from N2O

24 |

5. Facebook Login with AVZ

25 |

6. Google Login

26 |

3. Publish our new Server for JavaScript team

27 |

1. New SSL Certificates

28 |

2. SPDY deploy and WebSockets over SSL

29 |

3. Implement and Test FULL OKEY protocol

30 |

4. Implement Pause in Games

31 |

5. Display Corner Cards

32 |

6. Have 8 Tashes

33 |

7. I saw Okey

34 |

8. Game Finalization

35 |

9. Chat

36 |

10. Test new transctions by playing standalone games

37 |

11. Test tournaments

38 |

12. Chanak and Timer display in tournaments

39 |

14. Fix get_player_info lost message

40 |

15. In next game red mark are not updated

41 |

4. Interconnect JavaScript Events to N2O Events

42 |

1. Make Bullet Connection for XHR Persistent

43 |

2. New Schema for Statisctic. Starts from Lucky

44 |

3. Persisted Games

45 |

4. Fix tests suites, make CT-compiant

46 | 1. Depending on the JavaScript team Thin or Thick client 47 | No need to design, just design new controls for Kakaranet Application 48 | 49 |

5. Finalizing The Client

50 | 51 |

1. Rework Skill Pointing and Statistic

52 |

2. Crate Game

53 |

3. Player Stats

54 |

4. In Game Chat

55 |

5. Drag & Drop Cards

56 |

6. Campaigns

57 | 58 |

6. Ramazan Sprint

59 | 60 |

1. Rank List

61 |

2. Table List

62 |

3. Notifications

63 |

4. Online Users List

64 |

5. Protocol Adjustments

65 |

6. Table Manager

66 | 67 |
68 | 69 |

Future

70 | 71 |

10. Tournaments

72 |

11. Copy Admin Functionality

73 |

12. Payments

74 |

13. Gift Shop

75 | 76 |
77 | 78 |
79 | 80 |
81 | 82 |
83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/svg_clean.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Placeholder for SVG Game Scene 12 | 13 |
14 |

15 | 16 |

17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |

35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/synrc.css: -------------------------------------------------------------------------------- 1 | 2 | A:link { color: blue; } 3 | A:visited { color: darkblue; } 4 | A:hover { color: blue; } 5 | 6 | .controls { display: none; visibility:hidden; } 7 | 8 | svg { display: block; margin: auto; } 9 | 10 | SUP { font-size:10pt; } 11 | BODY { font-size:14pt; font-family: "Exo 2",Calibri, Lucida Sans, "Lucida Grande"; } 12 | TD { font-family: margin: 0 auto; padding-right: 20px; } 13 | H1 { font-size:30pt; color: blue; font-weight: bold; font-size:30pt; margin-top:20px; margin-bottom:10px; margin-right:6px; } 14 | H2 { font-size:20pt; color: darkblue; margin-top:20px; margin-bottom:10px; margin-right:6px; } 15 | H3 { font-size:18pt; margin-top:20px; margin-bottom:10px; margin-right:6px; } 16 | P { font-size:14pt; margin-top: 0px; margin-bottom: 8px; } 17 | H3 P { font-size:16pt; margin-left: 70px; margin-top:20px; margin-bottom:10px; margin-right:6px; } 18 | B { font-size:14pt; background-color: lightblue; padding: 2px; } 19 | H2 B { font-size:16pt; background-color: lightblue; padding: 5px; margin-top: 20px; margin-bottom: 20px;} 20 | LI { font-size:14pt; } 21 | 22 | button { display: none; visibility:hidden;} 23 | label { display: none; visibility:hidden;} 24 | input { display: none; visibility:hidden;} 25 | select { display: none; visibility:hidden;} 26 | 27 | pre { font-size:10pt; font-family: Lucida Console; } 28 | 29 | .menu { font-size:14pt; font-family: Calibri, Lucida Sans Unicode; color: red; } 30 | .small_infoblock { font-family: Calibri, Lucida Sans Unicode; font-size:8pt; } 31 | .block { background-color: #EEEEEE; padding-top:6;padding-bottom:10;padding-right:10;padding-left:10;} 32 | 33 | .threecol { width:1300px; float:left; } 34 | .left { float:left; } 35 | .hints { width:59px;float:left;margin-right:6px; } 36 | .main { width:843px;float:left;margin-right:26px; margin-left: 50px; } 37 | .contents { font-size: 19pt; width:200px;float:left; } 38 | 39 | code { background-color: lightblue; margin: 15px 0px 20px 20px; 40 | padding: 15px 30px 20px 20px; display: inline-block; 41 | white-space: pre; } 42 | 43 | .numbox { padding-left:10px;padding-right:10px;padding-top:10px;padding-bottom:10px;background-color:black;color:white; } 44 | 45 | 46 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/templates/ButtonAnimation.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/templates/Card.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 10 | {this.value} 14 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/templates/Mustafa-Persona.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | 10 | 16 | 29 | 35 | 42 | 49 | 55 | 61 | 70 | 71 | -------------------------------------------------------------------------------- /apps/web/priv/static/doc/templates/Mustafa-Selection.svg: -------------------------------------------------------------------------------- 1 | 2 | 25 | 26 | -------------------------------------------------------------------------------- /apps/web/priv/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{title}} 6 | 7 | 8 | {{body}} 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | -------------------------------------------------------------------------------- /apps/web/rebar.config: -------------------------------------------------------------------------------- 1 | {deps, [ 2 | {erlydtl, ".*", {git, "git://github.com/evanmiller/erlydtl", {tag,"0.8.0"}}}, 3 | {n2o, ".*", {git, "git://github.com/5HT/n2o", {tag,"1.5.0"}}}, 4 | server 5 | ]}. 6 | 7 | {erlydtl_opts, [ 8 | {doc_root, "priv/templates"}, 9 | {out_dir, "ebin"}, 10 | {compiler_options, [report, return, debug_info]}, 11 | {source_ext, ".html"}, 12 | {module_ext, "_view"} 13 | ]}. 14 | -------------------------------------------------------------------------------- /apps/web/src/js_session.erl: -------------------------------------------------------------------------------- 1 | -module(js_session). 2 | -author('Maxim Sokhatsky'). 3 | -include_lib("n2o/include/wf.hrl"). 4 | -include_lib("kvs/include/user.hrl"). 5 | -include_lib("stdlib/include/ms_transform.hrl"). 6 | -export(?SESSION_API). 7 | -compile(export_all). 8 | -record(state, {unique, node}). 9 | 10 | init(State,Ctx) -> {ok,State,Ctx}. 11 | finish(State,Ctx) -> {ok,State,Ctx}. 12 | 13 | ensure_sid(State, Ctx) -> 14 | SessionUser = wf:cookie_req(<<"n2o-name">>,?REQ), 15 | SessionId = wf:cookie_req(<<"n2o-sid">>, ?REQ), 16 | wf:info(?MODULE,"Session Init n2o-sid: ~p",[SessionId]), 17 | {{D1,D2,D3},{T1,T2,T3}} = calendar:now_to_datetime(now()), 18 | Till = {{D1,D2,D3+1},{T1,T2,T3}}, 19 | TTL = 24 * 60 * 60, % 1 day TTL 20 | SessionCookie = case lookup_ets({SessionId,<<"auth">>}) of 21 | undefined -> 22 | CookieValue = case kvs:get(user,SessionUser) of 23 | {ok,User} -> 24 | SS = lists:keyfind(n2o,1,User#user.tokens), 25 | case SS of 26 | {n2o,SessionId} -> SessionId; 27 | _ -> new_cookie_value() end; 28 | _ -> new_cookie_value() end, 29 | Cookie = {{CookieValue,<<"auth">>},<<"/">>,now(),{TTL,Till},new}, 30 | ets:insert(cookies,Cookie), 31 | wf:info(?MODULE,"Cookie New: ~p",[Cookie]), 32 | Cookie; 33 | {{Session,Key},Path,Issued,{_TTL,_Till},Status} -> 34 | case expired(Issued,{_TTL,_Till}) of 35 | false -> 36 | Cookie = {{Session,Key},Path,Issued,{_TTL,_Till},Status}, 37 | wf:info(?MODULE,"Cookie Same: ~p",[Cookie]), 38 | Cookie; 39 | true -> Cookie = {{new_cookie_value(),<<"auth">>},<<"/">>,now(),{TTL,Till},new}, 40 | ets:insert(cookies,Cookie), 41 | wf:info(?MODULE,"Cookie Expired: ~p",[Cookie]), 42 | Cookie end; 43 | _ -> error_logger:info_msg("Cookie Error"), skip 44 | end, 45 | {{ID,_},_,_,_,_} = SessionCookie, 46 | put(session_id,ID), 47 | wf:info(?MODULE,"State: ~p",[SessionCookie]), 48 | {ok, State, Ctx#context{session=SessionCookie}}. 49 | 50 | expired(_Issued,{_TTL,Till}) -> false. % Till < calendar:now_to_datetime(now()). 51 | 52 | lookup_ets(Key) -> 53 | Res = ets:lookup(cookies,Key), 54 | wf:info(?MODULE,"Lookup ETS: ~p",[{Res,Key}]), 55 | case Res of 56 | [] -> undefined; 57 | [Value] -> Value; 58 | Values -> Values end. 59 | 60 | clear() -> clear(session_id()). 61 | clear(Session) -> 62 | [ ets:delete(cookies,X) || X <- ets:select(cookies, 63 | ets:fun2ms(fun(A) when (element(1,element(1,A)) == Session) -> element(1,A) end)) ]. 64 | 65 | cookie_expire(SecondsToLive) -> 66 | Seconds = calendar:datetime_to_gregorian_seconds(calendar:local_time()), 67 | DateTime = calendar:gregorian_seconds_to_datetime(Seconds + SecondsToLive), 68 | httpd_util:rfc1123_date(DateTime). 69 | 70 | ttl() -> 60*60*24*30. 71 | 72 | session_id() -> get(session_id). 73 | new_cookie_value() -> 74 | SessionKey = base64:encode(erlang:md5(term_to_binary({now(), make_ref()}))), 75 | wf:wire(wf:f("document.cookie='~s=~s; path=/; expires=~s';", 76 | [wf:to_list(session_cookie_name()), 77 | wf:to_list(SessionKey), 78 | cookie_expire(ttl())])), 79 | SessionKey. 80 | 81 | new_cookie_value(SessionKey) -> 82 | wf:info(?MODULE,wf:f("document.cookie='~s=~s; path=/; expires=~s';", 83 | [wf:to_list(session_cookie_name()), 84 | wf:to_list(SessionKey), 85 | cookie_expire(ttl())]),[]), 86 | SessionKey. 87 | 88 | new_state() -> #state{unique=new_cookie_value()}. 89 | session_cookie_name() -> <<"n2o-sid">>. 90 | set_value(Key, Value) -> ets:insert(cookies,{{session_id(),Key},Value}), Value. 91 | get_value(Key, DefaultValue) -> 92 | Res = case lookup_ets({session_id(),Key}) of 93 | undefined -> DefaultValue; 94 | {_,Value} -> Value end, 95 | wf:info(?MODULE,"Session Lookup Key ~p Value ~p",[Key,Res]), 96 | Res. 97 | -------------------------------------------------------------------------------- /apps/web/src/logallow.erl: -------------------------------------------------------------------------------- 1 | -module(logallow). 2 | -compile(export_all). 3 | 4 | log_modules() -> [ 5 | % wf_core, 6 | n2o_bullet, 7 | routes, 8 | % okey_bot, 9 | game_session, 10 | % bullet_handler, 11 | okey_table, 12 | okey_desk, 13 | okey_scoring, 14 | journal, 15 | % lucky, 16 | % n2o_secret, 17 | js_session, 18 | okey 19 | ]. 20 | -------------------------------------------------------------------------------- /apps/web/src/protocol.erl: -------------------------------------------------------------------------------- 1 | -module(protocol). 2 | -compile({parse_transform, shen}). 3 | -compile(export_all). 4 | -jsmacro([take/2,attach/1,join/1,discard/3,player_info/2,reveal/4, 5 | piece/2,logout/0,pause/2,i_saw_okey/1,i_have_8_tashes/1]). 6 | 7 | attach(Token) -> ws:send(enc(tuple(atom('client'),tuple(atom("session_attach"), Token)))). 8 | join(Game) -> ws:send(enc(tuple(atom('client'),tuple(atom("join_game"), Game)))). 9 | logout() -> ws:send(enc(tuple(atom('client'),tuple(atom("logout"))))). 10 | pause(GameId, Action) -> ws:send(enc(tuple(atom('client'),tuple(atom("pause_game"),atom('undefined'),GameId,atom(Action))))). 11 | player_info(User,GameModule) -> ws:send(enc(tuple(atom('client'),tuple(atom("stats_action"),bin(User),atom(GameModule))))). 12 | 13 | take(GameId,Place) -> ws:send(enc(tuple(atom('client'),tuple(atom("game_action"),GameId,atom("okey_take"),[{pile,Place}])))). 14 | discard(GameId, Color, Value) -> ws:send(enc(tuple(atom('client'),tuple(atom("game_action"),GameId,atom("okey_discard"), 15 | [{tile,tuple(atom("OkeyPiece"), Color, Value)}])))). 16 | piece(Color,Value) -> tuple(atom("OkeyPiece"), Color, Value). 17 | reveal(GameId, Color, Value, Hand) -> 18 | ws:send(enc(tuple(atom('client'),tuple(atom("game_action"),GameId,atom("okey_reveal"), 19 | [{discarded, tuple(atom("OkeyPiece"), Color, Value)},{hand, Hand}])))). 20 | i_saw_okey(GameId) -> ws:send(enc(tuple(atom('client'),tuple(atom("game_action"),GameId,atom("okey_i_saw_okey"),[])))). 21 | i_have_8_tashes(GameId) -> ws:send(enc(tuple(atom('client'),tuple(atom("game_action"),GameId,atom("okey_i_have_8_tashes"),[])))). 22 | 23 | -------------------------------------------------------------------------------- /apps/web/src/routes.erl: -------------------------------------------------------------------------------- 1 | -module (routes). 2 | -author('Maxim Sokhatsky'). 3 | -include_lib("n2o/include/wf.hrl"). 4 | -export(?ROUTING_API). 5 | 6 | finish(State, Ctx) -> {ok, State, Ctx}. 7 | init(State, Ctx) -> 8 | Path = wf:path(Ctx#context.req), 9 | wf:info("ROute: ~p",[Path]), 10 | 11 | Module = route_prefix(Path), 12 | {ok, State, Ctx#context{path=Path,module=Module}}. 13 | 14 | route_prefix(<<"/ws/",P/binary>>) -> route(P); 15 | route_prefix(<<"/",P/binary>>) -> route(P); 16 | route_prefix(P) -> route(P). 17 | 18 | route(<<>>) -> okey; 19 | route(<<"index">>) -> okey; 20 | route(<<"favicon.ico">>) -> static_file; 21 | route(_) -> okey. 22 | -------------------------------------------------------------------------------- /apps/web/src/web.app.src: -------------------------------------------------------------------------------- 1 | {application, web, 2 | [ 3 | {description, "KKN Web Site"}, 4 | {vsn, "1"}, 5 | {registered, []}, 6 | {applications, [kernel, stdlib]}, 7 | {mod, { web_app, []}}, 8 | {env, []} 9 | ]}. 10 | -------------------------------------------------------------------------------- /apps/web/src/web_app.erl: -------------------------------------------------------------------------------- 1 | -module(web_app). 2 | -behaviour(application). 3 | -export([start/2, stop/1]). 4 | 5 | start(_StartType, _StartArgs) -> 6 | application:start(crypto), 7 | application:start(sasl), 8 | application:start(ranch), 9 | application:start(cowlib), 10 | application:start(cowboy), 11 | application:start(gproc), 12 | application:start(syntax_tools), 13 | application:start(compiler), 14 | application:start(erlydtl), 15 | application:start(rest), 16 | application:start(n2o), 17 | 18 | web_sup:start_link(). 19 | 20 | stop(_State) -> ok. 21 | -------------------------------------------------------------------------------- /apps/web/src/web_sup.erl: -------------------------------------------------------------------------------- 1 | -module(web_sup). 2 | -behaviour(supervisor). 3 | -export([start_link/0, init/1]). 4 | -compile(export_all). 5 | -include_lib ("n2o/include/wf.hrl"). 6 | -include("users.hrl"). 7 | -define(APP, web). 8 | 9 | start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). 10 | 11 | init([]) -> 12 | 13 | {ok, _} = cowboy:start_http(http, 100, [{port, wf:config(n2o,transition_port,8080)}], 14 | [{env, [{dispatch, dispatch_rules()}]}]), 15 | 16 | case file:read_file("/home/kakauser/cert/2014/startssl.ca") of 17 | {error,_} -> skip; 18 | {ok,_} -> 19 | {ok, _} = cowboy:start_https(https, 100, [ 20 | {port, wf:config(n2o,port,8443)}, 21 | {cacertfile, "/home/kakauser/cert/2014/startssl.ca"}, 22 | {certfile, "/home/kakauser/cert/2014/kakaranet.com.crt"}, 23 | {keyfile, "/home/kakauser/cert/2014/kakaranet.dec.key"} ], 24 | [{env, [{dispatch, dispatch_rules()}]}]) 25 | end, 26 | 27 | 28 | {ok, {{one_for_one, 5, 10}, []}}. 29 | 30 | dispatch_rules() -> 31 | cowboy_router:compile( 32 | [{'_', [ 33 | {"/static/[...]", cowboy_static, 34 | {priv_dir, ?APP, <<"static">>,[{mimetypes,cow_mimetypes,all}]}}, 35 | {"/rest/:resource", rest_cowboy, []}, 36 | {"/rest/:resource/:id", rest_cowboy, []}, 37 | {"/ws/[...]", bullet_handler, [{handler, n2o_bullet}]}, 38 | {'_', n2o_cowboy, []} 39 | ]}]). 40 | -------------------------------------------------------------------------------- /mad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devaspot/games/a1f7c3169c53d31e56049e90e0094a3f309603ae/mad -------------------------------------------------------------------------------- /orderapps.erl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | % This script boots up the Reltool Server for apps ordering 4 | % It also could generate reltool.config 5 | 6 | -module(orderapps). 7 | -compile([export_all]). 8 | 9 | relconfig(Apps) -> 10 | LibDirs = [Dir || Dir <- ["apps", "deps"], case file:read_file_info(Dir) of {ok, _} -> true; _ -> false end], 11 | {sys, [{lib_dirs,LibDirs}, {rel,"node","1",Apps}, {profile, embedded}, 12 | {boot_rel,"node"}, {app,observer,[{incl_cond,exclude}]} ]}. 13 | 14 | main([]) -> io:format("usage: ./orderapps.erl apps~n", []); 15 | main(MainApps) -> 16 | Relconfig = relconfig([list_to_atom(A) || A <- MainApps]), 17 | {ok, Server} = reltool:start_server([{config, Relconfig}]), 18 | {ok, {release, _Node, _Erts, Apps}} = reltool_server:get_rel(Server, "node"), 19 | Ordered = [element(1, A) || A <- Apps], 20 | io:format("~w~n", [Ordered]). 21 | -------------------------------------------------------------------------------- /otp.mk: -------------------------------------------------------------------------------- 1 | VM := rels/web/files/vm.args 2 | SYS := rels/web/files/sys.config 3 | PLT_NAME := ~/.n2o_dialyzer.plt 4 | ERL_ARGS := -args_file $(VM) -config $(SYS) 5 | RUN_DIR ?= rels/web/devbox 6 | LOG_DIR ?= rels/web/devbox/logs 7 | empty := 8 | ROOTS := apps deps 9 | space := $(empty) $(empty) 10 | comma := $(empty),$(empty) 11 | VSN := $(shell git rev-parse HEAD | head -c 6) 12 | DATE := $(shell date "+%Y%m%d-%H%M%S") 13 | ERL_LIBS := $(subst $(space),:,$(ROOTS)) 14 | relx := "{release,{$(RELEASE),\"$(VER)\"},[$(RELEASE)]}.\\n{include_erts,true}.\ 15 | \\n{extended_start_script,true}.\\n{generate_start_script,true}.\\n{sys_config,\"$(SYS)\"}.\ 16 | \\n{vm_args,\"$(VM)\"}.\\n{overlay,[{mkdir,\"log/sasl\"}]}." 17 | 18 | test: eunit ct 19 | compile: get-deps 20 | delete-deps get-deps compile update-deps: 21 | rebar $@ 22 | clean: 23 | rm -f .applist 24 | rebar $@ 25 | .applist: 26 | $(eval APPS := $(subst deps/,,$(subst apps/,,$(shell find apps deps -maxdepth 1 -mindepth 1 -type d)))) 27 | ./orderapps.erl $(APPS) > $@ 28 | $(RUN_DIR) $(LOG_DIR): 29 | mkdir -p $(RUN_DIR) & mkdir -p $(LOG_DIR) 30 | console: .applist 31 | ERL_LIBS=$(ERL_LIBS) erl +pc unicode $(ERL_ARGS) -eval '[application:start(A) || A <- $(shell cat .applist)]' 32 | start: $(RUN_DIR) $(LOG_DIR) .applist 33 | RUN_ERL_LOG_GENERATIONS=1000 RUN_ERL_LOG_MAXSIZE=20000000 \ 34 | ERL_LIBS=$(ERL_LIBS) run_erl -daemon $(RUN_DIR)/ $(LOG_DIR)/ "exec $(MAKE) console" 35 | attach: 36 | to_erl $(RUN_DIR)/ 37 | release: 38 | echo $(relx) > relx.config && relx 39 | stop: 40 | @kill -9 $(shell ps ax -o pid= -o command=|grep $(RELEASE)|grep $(COOKIE)|awk '{print $$1}') 41 | $(PLT_NAME): 42 | $(eval APPS := $(subst deps/,,$(subst apps/,,$(shell find apps deps -maxdepth 1 -mindepth 1 -type d)))) 43 | ERL_LIBS=$(ERL_LIBS) dialyzer --build_plt --output_plt $(PLT_NAME) --apps $(APPS) || true 44 | dialyze: $(PLT_NAME) compile 45 | $(eval APPS := $(shell find apps deps -maxdepth 1 -mindepth 1 -type d)) 46 | @$(foreach var,$(APPS),(echo "Process $(var)"; dialyzer -q $(var)/ebin --plt $(PLT_NAME) --no_native -Werror_handling -Wunderspecs -Wrace_conditions -Wno_undefined_callbacks);) 47 | tar: release 48 | tar zcvf $(RELEASE)-$(VSN)-$(DATE).tar.gz _rel/lib/*/ebin _rel/lib/*/priv _rel/bin _rel/releases 49 | eunit: 50 | rebar eunit skip_deps=true 51 | ct: 52 | rebar ct skip_deps=true verbose=1 53 | 54 | .PHONY: delete-deps get-deps compile clean console start attach release update-deps dialyze ct eunit tar 55 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {sub_dirs,["apps"]}. 2 | {lib_dirs,["apps"]}. 3 | {deps_dir,"deps"}. 4 | {deps, [ 5 | {shen, ".*", {git, "git://github.com/5HT/shen", {tag, "1.5.0"} }}, 6 | {n2o, ".*", {git, "git://github.com/synrc/n2o", {tag, "1.8.0"} }}, 7 | {erlydtl,".*", {git, "git://github.com/evanmiller/erlydtl", {tag, "0.8.0"} }}, 8 | {cowboy, ".*", {git, "git://github.com/extend/cowboy", {tag, "0.9.0"} }}, 9 | {gproc, ".*", {git, "git://github.com/uwiger/gproc.git", {tag, "0.3"} }}, 10 | {fs, ".*", {git, "git://github.com/synrc/fs", {tag, "master"} }}, 11 | {sh, ".*", {git, "git://github.com/synrc/sh", {tag, "master"} }}, 12 | {active, ".*", {git, "git://github.com/synrc/active", {tag, "master"} }}, 13 | {mad, ".*", {git, "git://github.com/synrc/mad", {tag, "master"} }}, 14 | {rest, ".*", {git, "git://github.com/synrc/rest", {tag, "1.5.0"} }}, 15 | {kvs, ".*", {git, "git://github.com/synrc/kvs", {tag, "1.5.0"} }}, 16 | {avz, ".*", {git, "git://github.com/synrc/avz", {tag, "1.4.0"} }} 17 | ]}. 18 | -------------------------------------------------------------------------------- /rels/web/files/erl: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ## This script replaces the default "erl" in erts-VSN/bin. This is necessary 4 | ## as escript depends on erl and in turn, erl depends on having access to a 5 | ## bootscript (start.boot). Note that this script is ONLY invoked as a side-effect 6 | ## of running escript -- the embedded node bypasses erl and uses erlexec directly 7 | ## (as it should). 8 | ## 9 | ## Note that this script makes the assumption that there is a start_clean.boot 10 | ## file available in $ROOTDIR/release/VSN. 11 | 12 | # Determine the abspath of where this script is executing from. 13 | ERTS_BIN_DIR=$(cd ${0%/*} && pwd) 14 | 15 | # Now determine the root directory -- this script runs from erts-VSN/bin, 16 | # so we simply need to strip off two dirs from the end of the ERTS_BIN_DIR 17 | # path. 18 | ROOTDIR=${ERTS_BIN_DIR%/*/*} 19 | 20 | # Parse out release and erts info 21 | START_ERL=`cat $ROOTDIR/releases/start_erl.data` 22 | ERTS_VSN=${START_ERL% *} 23 | APP_VSN=${START_ERL#* } 24 | 25 | BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin 26 | EMU=beam 27 | PROGNAME=`echo $0 | sed 's/.*\\///'` 28 | CMD="$BINDIR/erlexec" 29 | export EMU 30 | export ROOTDIR 31 | export BINDIR 32 | export PROGNAME 33 | 34 | exec $CMD -boot $ROOTDIR/releases/$APP_VSN/start_clean ${1+"$@"} 35 | -------------------------------------------------------------------------------- /rels/web/files/install_upgrade.escript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %%! -noshell -noinput 3 | %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- 4 | %% ex: ft=erlang ts=4 sw=4 et 5 | 6 | -define(TIMEOUT, 60000). 7 | -define(INFO(Fmt,Args), io:format(Fmt,Args)). 8 | 9 | main([NodeName, Cookie, ReleasePackage]) -> 10 | TargetNode = start_distribution(NodeName, Cookie), 11 | {ok, Vsn} = rpc:call(TargetNode, release_handler, unpack_release, 12 | [ReleasePackage], ?TIMEOUT), 13 | ?INFO("Unpacked Release ~p~n", [Vsn]), 14 | {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler, 15 | check_install_release, [Vsn], ?TIMEOUT), 16 | {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler, 17 | install_release, [Vsn], ?TIMEOUT), 18 | ?INFO("Installed Release ~p~n", [Vsn]), 19 | ok = rpc:call(TargetNode, release_handler, make_permanent, [Vsn], ?TIMEOUT), 20 | ?INFO("Made Release ~p Permanent~n", [Vsn]); 21 | main(_) -> 22 | init:stop(1). 23 | 24 | start_distribution(NodeName, Cookie) -> 25 | MyNode = make_script_node(NodeName), 26 | {ok, _Pid} = net_kernel:start([MyNode, shortnames]), 27 | erlang:set_cookie(node(), list_to_atom(Cookie)), 28 | TargetNode = make_target_node(NodeName), 29 | case {net_kernel:hidden_connect_node(TargetNode), 30 | net_adm:ping(TargetNode)} of 31 | {true, pong} -> 32 | ok; 33 | {_, pang} -> 34 | io:format("Node ~p not responding to pings.\n", [TargetNode]), 35 | init:stop(1) 36 | end, 37 | TargetNode. 38 | 39 | make_target_node(Node) -> 40 | [_, Host] = string:tokens(atom_to_list(node()), "@"), 41 | list_to_atom(lists:concat([Node, "@", Host])). 42 | 43 | make_script_node(Node) -> 44 | list_to_atom(lists:concat([Node, "_upgrader_", os:getpid()])). 45 | -------------------------------------------------------------------------------- /rels/web/files/node.cmd: -------------------------------------------------------------------------------- 1 | @setlocal 2 | 3 | @set node_name=node 4 | 5 | @rem Get the absolute path to the parent directory, 6 | @rem which is assumed to be the node root. 7 | @for /F "delims=" %%I in ("%~dp0..") do @set node_root=%%~fI 8 | 9 | @set releases_dir=%node_root%\releases 10 | 11 | @rem Parse ERTS version and release version from start_erl.data 12 | @for /F "usebackq tokens=1,2" %%I in ("%releases_dir%\start_erl.data") do @( 13 | @call :set_trim erts_version %%I 14 | @call :set_trim release_version %%J 15 | ) 16 | 17 | @set vm_args=%releases_dir%\%release_version%\vm.args 18 | @set sys_config=%releases_dir%\%release_version%\sys.config 19 | @set node_boot_script=%releases_dir%\%release_version%\%node_name% 20 | @set clean_boot_script=%releases_dir%\%release_version%\start_clean 21 | 22 | @rem extract erlang cookie from vm.args 23 | @for /f "usebackq tokens=1-2" %%I in (`findstr /b \-setcookie "%vm_args%"`) do @set erlang_cookie=%%J 24 | 25 | @set erts_bin=%node_root%\erts-%erts_version%\bin 26 | 27 | @set service_name=%node_name%_%release_version% 28 | 29 | @set erlsrv="%erts_bin%\erlsrv.exe" 30 | @set epmd="%erts_bin%\epmd.exe" 31 | @set escript="%erts_bin%\escript.exe" 32 | @set werl="%erts_bin%\werl.exe" 33 | 34 | @if "%1"=="usage" @goto usage 35 | @if "%1"=="install" @goto install 36 | @if "%1"=="uninstall" @goto uninstall 37 | @if "%1"=="start" @goto start 38 | @if "%1"=="stop" @goto stop 39 | @if "%1"=="restart" @call :stop && @goto start 40 | @if "%1"=="console" @goto console 41 | @if "%1"=="query" @goto query 42 | @if "%1"=="attach" @goto attach 43 | @if "%1"=="upgrade" @goto upgrade 44 | @echo Unknown command: "%1" 45 | 46 | :usage 47 | @echo Usage: %~n0 [install^|uninstall^|start^|stop^|restart^|console^|query^|attach^|upgrade] 48 | @goto :EOF 49 | 50 | :install 51 | @set description=Erlang node %node_name% in %node_root% 52 | @set start_erl=%node_root%\bin\start_erl.cmd 53 | @set args= ++ %node_name% ++ %node_root% 54 | @%erlsrv% add %service_name% -c "%description%" -sname %node_name% -w "%node_root%" -m "%start_erl%" -args "%args%" -stopaction "init:stop()." 55 | @goto :EOF 56 | 57 | :uninstall 58 | @%erlsrv% remove %service_name% 59 | @%epmd% -kill 60 | @goto :EOF 61 | 62 | :start 63 | @%erlsrv% start %service_name% 64 | @goto :EOF 65 | 66 | :stop 67 | @%erlsrv% stop %service_name% 68 | @goto :EOF 69 | 70 | :console 71 | @start "%node_name% console" %werl% -boot "%node_boot_script%" -config "%sys_config%" -args_file "%vm_args%" -sname %node_name% 72 | @goto :EOF 73 | 74 | :query 75 | @%erlsrv% list %service_name% 76 | @exit %ERRORLEVEL% 77 | @goto :EOF 78 | 79 | :attach 80 | @for /f "usebackq" %%I in (`hostname`) do @set hostname=%%I 81 | start "%node_name% attach" %werl% -boot "%clean_boot_script%" -remsh %node_name%@%hostname% -sname console -setcookie %erlang_cookie% 82 | @goto :EOF 83 | 84 | :upgrade 85 | @if "%2"=="" ( 86 | @echo Missing upgrade package argument 87 | @echo Usage: %~n0 upgrade {package base name} 88 | @echo NOTE {package base name} MUST NOT include the .tar.gz suffix 89 | @goto :EOF 90 | ) 91 | @%escript% %node_root%\bin\install_upgrade.escript %node_name% %erlang_cookie% %2 92 | @goto :EOF 93 | 94 | :set_trim 95 | @set %1=%2 96 | @goto :EOF 97 | -------------------------------------------------------------------------------- /rels/web/files/nodetool: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- 2 | %% ex: ft=erlang ts=4 sw=4 et 3 | %% ------------------------------------------------------------------- 4 | %% 5 | %% nodetool: Helper Script for interacting with live nodes 6 | %% 7 | %% ------------------------------------------------------------------- 8 | 9 | main(Args) -> 10 | ok = start_epmd(), 11 | %% Extract the args 12 | {RestArgs, TargetNode} = process_args(Args, [], undefined), 13 | 14 | %% See if the node is currently running -- if it's not, we'll bail 15 | case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of 16 | {true, pong} -> 17 | ok; 18 | {_, pang} -> 19 | io:format("Node ~p not responding to pings.\n", [TargetNode]), 20 | halt(1) 21 | end, 22 | 23 | case RestArgs of 24 | ["ping"] -> 25 | %% If we got this far, the node already responsed to a ping, so just dump 26 | %% a "pong" 27 | io:format("pong\n"); 28 | ["stop"] -> 29 | io:format("~p\n", [rpc:call(TargetNode, init, stop, [], 60000)]); 30 | ["restart"] -> 31 | io:format("~p\n", [rpc:call(TargetNode, init, restart, [], 60000)]); 32 | ["reboot"] -> 33 | io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]); 34 | ["rpc", Module, Function | RpcArgs] -> 35 | case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), 36 | [RpcArgs], 60000) of 37 | ok -> 38 | ok; 39 | {badrpc, Reason} -> 40 | io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), 41 | halt(1); 42 | _ -> 43 | halt(1) 44 | end; 45 | ["rpcterms", Module, Function, ArgsAsString] -> 46 | case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), 47 | consult(ArgsAsString), 60000) of 48 | {badrpc, Reason} -> 49 | io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), 50 | halt(1); 51 | Other -> 52 | io:format("~p\n", [Other]) 53 | end; 54 | Other -> 55 | io:format("Other: ~p\n", [Other]), 56 | io:format("Usage: nodetool {ping|stop|restart|reboot}\n") 57 | end, 58 | net_kernel:stop(). 59 | 60 | process_args([], Acc, TargetNode) -> 61 | {lists:reverse(Acc), TargetNode}; 62 | process_args(["-setcookie", Cookie | Rest], Acc, TargetNode) -> 63 | erlang:set_cookie(node(), list_to_atom(Cookie)), 64 | process_args(Rest, Acc, TargetNode); 65 | process_args(["-name", TargetName | Rest], Acc, _) -> 66 | ThisNode = append_node_suffix(TargetName, "_maint_"), 67 | {ok, _} = net_kernel:start([ThisNode, longnames]), 68 | process_args(Rest, Acc, nodename(TargetName)); 69 | process_args(["-sname", TargetName | Rest], Acc, _) -> 70 | ThisNode = append_node_suffix(TargetName, "_maint_"), 71 | {ok, _} = net_kernel:start([ThisNode, shortnames]), 72 | process_args(Rest, Acc, nodename(TargetName)); 73 | process_args([Arg | Rest], Acc, Opts) -> 74 | process_args(Rest, [Arg | Acc], Opts). 75 | 76 | 77 | start_epmd() -> 78 | [] = os:cmd(epmd_path() ++ " -daemon"), 79 | ok. 80 | 81 | epmd_path() -> 82 | ErtsBinDir = filename:dirname(escript:script_name()), 83 | Name = "epmd", 84 | case os:find_executable(Name, ErtsBinDir) of 85 | false -> 86 | case os:find_executable(Name) of 87 | false -> 88 | io:format("Could not find epmd.~n"), 89 | halt(1); 90 | GlobalEpmd -> 91 | GlobalEpmd 92 | end; 93 | Epmd -> 94 | Epmd 95 | end. 96 | 97 | 98 | nodename(Name) -> 99 | case string:tokens(Name, "@") of 100 | [_Node, _Host] -> 101 | list_to_atom(Name); 102 | [Node] -> 103 | [_, Host] = string:tokens(atom_to_list(node()), "@"), 104 | list_to_atom(lists:concat([Node, "@", Host])) 105 | end. 106 | 107 | append_node_suffix(Name, Suffix) -> 108 | case string:tokens(Name, "@") of 109 | [Node, Host] -> 110 | list_to_atom(lists:concat([Node, Suffix, os:getpid(), "@", Host])); 111 | [Node] -> 112 | list_to_atom(lists:concat([Node, Suffix, os:getpid()])) 113 | end. 114 | 115 | 116 | %% 117 | %% Given a string or binary, parse it into a list of terms, ala file:consult/0 118 | %% 119 | consult(Str) when is_list(Str) -> 120 | consult([], Str, []); 121 | consult(Bin) when is_binary(Bin)-> 122 | consult([], binary_to_list(Bin), []). 123 | 124 | consult(Cont, Str, Acc) -> 125 | case erl_scan:tokens(Cont, Str, 0) of 126 | {done, Result, Remaining} -> 127 | case Result of 128 | {ok, Tokens, _} -> 129 | {ok, Term} = erl_parse:parse_term(Tokens), 130 | consult([], Remaining, [Term | Acc]); 131 | {eof, _Other} -> 132 | lists:reverse(Acc); 133 | {error, Info, _} -> 134 | {error, Info} 135 | end; 136 | {more, Cont1} -> 137 | consult(Cont1, eof, Acc) 138 | end. 139 | -------------------------------------------------------------------------------- /rels/web/files/start_erl.cmd: -------------------------------------------------------------------------------- 1 | @setlocal 2 | 3 | @rem Parse arguments. erlsrv.exe prepends erl arguments prior to first ++. 4 | @rem Other args are position dependent. 5 | @set args="%*" 6 | @for /F "delims=++ tokens=1,2,3" %%I in (%args%) do @( 7 | @set erl_args=%%I 8 | @call :set_trim node_name %%J 9 | @rem Trim spaces from the left of %%K (node_root), which may have spaces inside 10 | @for /f "tokens=* delims= " %%a in ("%%K") do @set node_root=%%a 11 | ) 12 | 13 | @set releases_dir=%node_root%\releases 14 | 15 | @rem parse ERTS version and release version from start_erl.dat 16 | @for /F "usebackq tokens=1,2" %%I in ("%releases_dir%\start_erl.data") do @( 17 | @call :set_trim erts_version %%I 18 | @call :set_trim release_version %%J 19 | ) 20 | 21 | @set erl_exe="%node_root%\erts-%erts_version%\bin\erl.exe" 22 | @set boot_file="%releases_dir%\%release_version%\%node_name%" 23 | 24 | @if exist "%releases_dir%\%release_version%\sys.config" ( 25 | @set app_config="%releases_dir%\%release_version%\sys.config" 26 | ) else ( 27 | @set app_config="%node_root%\etc\app.config" 28 | ) 29 | 30 | @if exist "%releases_dir%\%release_version%\vm.args" ( 31 | @set vm_args="%releases_dir%\%release_version%\vm.args" 32 | ) else ( 33 | @set vm_args="%node_root%\etc\vm.args" 34 | ) 35 | 36 | @%erl_exe% %erl_args% -boot %boot_file% -config %app_config% -args_file %vm_args% 37 | 38 | :set_trim 39 | @set %1=%2 40 | @goto :EOF 41 | -------------------------------------------------------------------------------- /rels/web/files/sys.config: -------------------------------------------------------------------------------- 1 | [ 2 | {sync, [{sync_mode, nitrogen}]}, 3 | {nsx_idgen, [{game_pool,5000000}]}, 4 | {n2o, [{route,routes}, 5 | {port,8443}, 6 | {log_modules,logallow}, 7 | {session,js_session}, 8 | {pickler,n2o_secret}]}, 9 | {server, [{log_modules,logallow}]}, 10 | {face,[ 11 | {http_address, "http://skyline.synrc.com"}, 12 | {gplus_client_id, "146782506820.apps.googleusercontent.com"}, 13 | {gplus_cookiepolicy, "http://synrc.com"}]}, 14 | {kvs, 15 | [{pass_init_db,true}, 16 | {nodes,[]}, 17 | {log_modules,logallow}, 18 | {dba, store_mnesia}, 19 | {schema, [kvs_user, kvs_acl, kvs_feed, kvs_subscription, 20 | db_config, db_scoring, db_accounts, db_table, db_tournaments, db_journal ]}]}, 21 | {sasl, [ 22 | {sasl_error_logger, {file, "log/sasl-error.log"}}, 23 | {errlog_type, error}, 24 | {error_logger_mf_dir, "log/sasl"}, % Log directory 25 | {error_logger_mf_maxbytes, 10485760}, % 10 MB max file size 26 | {error_logger_mf_maxfiles, 5} % 5 files max 27 | ]} 28 | ]. 29 | 30 | -------------------------------------------------------------------------------- /rels/web/files/vm.args: -------------------------------------------------------------------------------- 1 | -name kakaranet@127.0.0.1 2 | -setcookie node_runner 3 | +K true 4 | +A 5 5 | -env ERL_MAX_PORTS 4096 6 | -env ERL_FULLSWEEP_AFTER 10 7 | -------------------------------------------------------------------------------- /rels/web/reltool.config: -------------------------------------------------------------------------------- 1 | {sys, [ 2 | {lib_dirs, ["../../apps","../../deps"]}, 3 | {erts, [{mod_cond, derived}, {app_file, strip}]}, 4 | {app_file, strip}, 5 | {rel, "node", "1", 6 | [ 7 | kernel, 8 | stdlib, 9 | avz, 10 | sasl, 11 | shen, 12 | gproc, 13 | cowlib, 14 | n2o, 15 | erlydtl, 16 | db, 17 | server, 18 | sync, 19 | kvs, 20 | web 21 | ]}, 22 | {rel, "start_clean", "", 23 | [ 24 | kernel, 25 | stdlib 26 | ]}, 27 | {boot_rel, "node"}, 28 | {profile, embedded}, 29 | {incl_cond, derived}, 30 | {mod_cond, derived}, 31 | {excl_archive_filters, [".*"]}, %% Do not archive built libs 32 | {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)", 33 | "^erts.*/(doc|info|include|lib|man|src)"]}, 34 | {excl_app_filters, ["\.gitignore"]}, 35 | {app, hipe, [{mod_cond, app}, {incl_cond, exclude}]}, 36 | {app, db, [{mod_cond, app}, {incl_cond, include}]} 37 | {app, server, [{mod_cond, app}, {incl_cond, include}]} 38 | {app, face, [{mod_cond, app}, {incl_cond, include}]} 39 | ]}. 40 | 41 | {target_dir, "node"}. 42 | 43 | {overlay, [ 44 | {mkdir, "log/sasl"}, 45 | {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"}, 46 | {copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"}, 47 | {copy, "files/node", "bin/node"}, 48 | {copy, "files/node.cmd", "bin/node.cmd"}, 49 | {copy, "files/start_erl.cmd", "bin/start_erl.cmd"}, 50 | {copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"}, 51 | {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"}, 52 | {copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"} 53 | ]}. 54 | --------------------------------------------------------------------------------