diff --git a/.gitignore b/.gitignore index d6f2bf3..55bdd6f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.dump *.beam Mnesia.* +*.swo diff --git a/HOWTO b/HOWTO deleted file mode 100644 index 2d28ea8..0000000 --- a/HOWTO +++ /dev/null @@ -1,21 +0,0 @@ -PREREQUISITES: -python version 2.x set to default. - -INSTALL -1. Cd into directory where you to have the project -2. git clone git@github.com:jeena/GGS.git (remember to have a local key) -3. cd GGS/ -4. git submodule init -5. git submodule update -6. cd erlang_js -7. make -8. make test (Optional. It has to work though.) -10. cd ../ -11. ./build -12. - -USAGE -1. start a second terminal -2. in new terminal do command: ./python_client 9000 -3. back to first terminal -4. ./start diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d40790a --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +ERLC=erlc +ERLCFLAGS=-o +SRCDIR=src +TESTDIR=tests +BEAMDIR=ebin + +all: compile erlang_js + +compile: + mkdir -p $(BEAMDIR) ; + $(ERLC) $(ERLCFLAGS) $(BEAMDIR) $(SRCDIR)/*.erl ; + +erlang_js: force_look + cd erlang_js ; $(MAKE) $(MFLAGS); + +test: + echo "==> test $(MOD)" ; + mkdir -p $(BEAMDIR) ; +ifeq ($(strip $(MOD)),) + $(ERLC) $(ERLCFLAGS) $(BEAMDIR) $(TESTDIR)/*.erl ; + cd $(BEAMDIR) ; erl -noinput -eval 'eunit:test({dir, "."}, [verbose]), init:stop()' ; +else + $(ERLC) $(ERLCFLAGS) $(BEAMDIR) $(TESTDIR)/$(MOD)_test.erl ; + cd $(BEAMDIR) ; erl -noinput -eval 'eunit:test($(MOD)_test, [verbose]), init:stop()' ; +endif + +clean: + rm -rf $(BEAMDIR)/*.beam ; + rm -rf erl_crush.dump ; + echo "==> clean ggs" ; + $(MAKE) -C erlang_js/ clean + +run: + erl \ + -sname ggs \ + -mnesia dir '"/tmp/ggs"' \ + -boot start_sasl \ + -pa erlang_js/ebin/ \ + -pa ebin \ + -pa src \ + -s start_ggs + +force_look: + true \ No newline at end of file diff --git a/README b/README index 853cc89..9aa5357 100644 --- a/README +++ b/README @@ -1,3 +1,35 @@ GGS is a Generic Game Server -Check out http://ggs-kandidat.blogspot.com/ \ No newline at end of file +Check out http://ggs-kandidat.blogspot.com/ + +PREREQUISITES: +python version 2.x set to default. + +INSTALL +1. cd into directory where you to have the project +2. git clone git@github.com:jeena/GGS.git (remember to have a local key) +3. cd GGS/ +4. git submodule init +5. git submodule update + +USAGE +1. start a second terminal +2. in new terminal do command: ./python_client 9000 +3. back to first terminal +4. make run + +MAKE +To compile modules (even erlang_js): + make + +To run server: + make run + +To clean (even erlang_js): + make clean + +To compile and run all tests: + make test + +To compile and run one test: + make test MOD=ggs_modulename # (must have /tests/ggs_modulename_test.erl) diff --git a/build b/build deleted file mode 100755 index a9c1c79..0000000 --- a/build +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -for i in `find src -name "*.erl"` -do - erlc -o ebin $i -done \ No newline at end of file diff --git a/build_test b/build_test deleted file mode 100755 index 2f9d630..0000000 --- a/build_test +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -./build - -for i in `find tests -name "*.erl"` -do - erlc -o ebin_test $i -done diff --git a/client b/client deleted file mode 100644 index dff11aa..0000000 --- a/client +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env ruby -wKU - -require 'socket' # Sockets are in standard library - -hostname = 'localhost' -port = 7000 - -s = TCPSocket.open(hostname, port) - - - -s.print(q.chop) - -while line = s.gets # Read lines from the socket - puts "Got Echo: " + line.chop # And print with platform line terminator -end -s.close # Close the socket when done diff --git a/echo_test b/echo_test deleted file mode 100755 index 1bbff1f..0000000 --- a/echo_test +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env ruby -wKU - -require 'socket' # Sockets are in standard library - -hostname = 'localhost' -port = 7000 - -print "Which port @ loclhost?" -port = gets - -s = TCPSocket.open(hostname, port.chop) - -s.print("__hello 0") - -while true - line = s.gets # Read lines from the socket - puts ">> " + line.chop # And print with platform line terminator - s.print(gets.chop) -end -s.close # Close the socket when done diff --git a/mnesia/gamedb.erl b/mnesia/gamedb.erl deleted file mode 100644 index 751eb92..0000000 --- a/mnesia/gamedb.erl +++ /dev/null @@ -1,50 +0,0 @@ -%%%%---------------------------------------------------- -%%% @author Mattias Pettersson -%%% @copyright 2011 Mattias Pettersson -%%% @doc Database for runtime game variable storage. -%%% @end - - Test Mnesia --module(gamedb). --import(mnesia). --export([init/0,insert_player/1,example_player/0,read_player/1,test_player/0]). --include("gamedb.hrl"). - -%%----------------------------------------------------- -%% Creation -%%----------------------------------------------------- -init() -> - mnesia:create_table(player, [{attributes, record_info(fields, player)}]). - -%%----------------------------------------------------- -%% Test -%%----------------------------------------------------- -test_player() -> - insert_player(example_player()), - read_player(0001). - -example_player() -> - #player{id = 0001, - name = "Tux"}. - -%%----------------------------------------------------- -%% Insertions -%%----------------------------------------------------- -insert_player(Player) -> - Fun = fun() -> - mnesia:write(Player) - end, - mnesia:transaction(Fun). - - -%%----------------------------------------------------- -%% Querries -%%----------------------------------------------------- -read_player(Player_Key) -> - Fun = fun() -> - [P] = mnesia:read(player, Player_Key), - Name = P#player.name, - io:format("Player name: ~s~n",[Name]) - end, - mnesia:transaction(Fun). - diff --git a/mnesia/gamedb.hrl b/mnesia/gamedb.hrl deleted file mode 100644 index 1ae9c8f..0000000 --- a/mnesia/gamedb.hrl +++ /dev/null @@ -1,6 +0,0 @@ -%% gamedb.hrl - --record(player, {id, name}). - - - diff --git a/mnesia/gamedb_usage.txt b/mnesia/gamedb_usage.txt deleted file mode 100644 index b3a07a0..0000000 --- a/mnesia/gamedb_usage.txt +++ /dev/null @@ -1,12 +0,0 @@ -1. From terminal: erl -mnesia dir '"/home/user/dir/to/GGS/GameDB.Player"' -2. mnesia:create_schema([node()]). -3. mnesia:start(). -4. c(gamedb). -5. gamedb:init(). -6. mnesia:info(). -7. gamedb:test_player(). - -Last output should be: - Player name: Tux - {atomic,ok} - diff --git a/src/ggs_coordinator.erl b/src/ggs_coordinator.erl index f1e7caa..535b9b0 100644 --- a/src/ggs_coordinator.erl +++ b/src/ggs_coordinator.erl @@ -91,7 +91,6 @@ handle_cast({stop, _Reason}, State) -> %% @TODO: Implement me %handle_cast({remove_player, Player}) -> % {noreply, State#co_state{ - handle_cast(_Message, State) -> {noreply, State}. diff --git a/src/ggs_db.erl b/src/ggs_db.erl new file mode 100644 index 0000000..29665a3 --- /dev/null +++ b/src/ggs_db.erl @@ -0,0 +1,91 @@ +%%%%---------------------------------------------------- +%%% @author Mattias Pettersson +%%% @copyright 2011 Mattias Pettersson +%%% @doc Database for runtime game variable storage. +%%% @end + +-module(ggs_db). +-export([init/0,stop/0,setItem/4,getItem/3,removeItem/3,key/3,clear/2,clear/1,length/2]). +%-include("ggs_db.hrl"). +-record(data, {key, value}). + +%%----------------------------------------------------- +%% Creation +%%----------------------------------------------------- +init() -> +% mnesia:create_schema([node()]), + mnesia:start(), + mnesia:create_table(data, [{attributes, record_info(fields, data)}]). + +stop() -> + mnesia:stop(). + + +%%----------------------------------------------------- +%% Insertions +%%----------------------------------------------------- +setItem(GameToken,Ns,Key,Value) -> + Fun = fun() -> + Data = #data{key = {GameToken,Ns,Key}, value = Value}, + mnesia:write(Data) + end, + mnesia:transaction(Fun). + + +%%----------------------------------------------------- +%% Deletions +%%----------------------------------------------------- +removeItem(GameToken,Ns,Key) -> + Fun = fun() -> + mnesia:delete({data,{GameToken,Ns,Key}}) + end, + mnesia:transaction(Fun). + +clear(GameToken,Ns) -> + Fun = fun() -> + Keys = mnesia:all_keys(data), + Rest = lists:filter(fun({A,B,_}) -> ((A==GameToken) and (B==Ns)) end, Keys), + lists:map(fun({A,B,C}) -> removeItem(A,B,C) end, Rest) + end, + {atomic, Ret} = mnesia:transaction(Fun), + Ret. + +clear(GameToken) -> + Fun = fun() -> + Keys = mnesia:all_keys(data), + Rest = lists:filter(fun({A,_,_}) -> (A==GameToken) end, Keys), + lists:map(fun({A,B,C}) -> removeItem(A,B,C) end, Rest) + end, + {atomic, Ret} = mnesia:transaction(Fun), + Ret. + +%%----------------------------------------------------- +%% Queries +%%----------------------------------------------------- +getItem(GameToken,Ns,Key) -> + Fun = fun() -> + mnesia:read(data, {GameToken,Ns,Key}) + end, + case mnesia:transaction(Fun) of + {atomic, []} -> + {error}; + {atomic, [Ret]} -> + Ret#data.value +end. + +length(GameToken,Ns) -> + Fun = fun() -> + Keys = mnesia:all_keys(data), + length(lists:filter(fun({A,B,_}) -> ((A==GameToken) and (B==Ns)) end, Keys)) + end, + {atomic, Ret} = mnesia:transaction(Fun), + Ret. + +key(GameToken,Ns,Position) -> + Fun = fun() -> + Keys = mnesia:all_keys(data), + Rest = lists:filter(fun({A,B,_}) -> ((A==GameToken) and (B==Ns)) end, Keys), + lists:nth(Position, Rest) + end, + {atomic, Ret} = mnesia:transaction(Fun), + Ret. diff --git a/src/ggs_db.hrl b/src/ggs_db.hrl new file mode 100644 index 0000000..b7f1f4c --- /dev/null +++ b/src/ggs_db.hrl @@ -0,0 +1,5 @@ +%% gamedb.hrl + +-record(data, {key, value}). + + diff --git a/src/ggs_gamevm.erl b/src/ggs_gamevm.erl index 6dd19a7..babee27 100644 --- a/src/ggs_gamevm.erl +++ b/src/ggs_gamevm.erl @@ -97,34 +97,3 @@ code_change(_OldVsn, State, _Extra) -> js_escape(S) -> lists:flatmap(fun($\') -> [$\\, $\']; (X) -> [X] end, S). -%% ---------------------------------------------------------------------- -% Tests - -start_link_test() -> - erlang_js:start(), %% @TODO: should only be done once - GameVM = start_link(test_table), - ?assertNot(GameVM =:= undefined). - -define_test() -> - GameVM = start_link(test_table), - define(GameVM, "function hello(test) { return test; }"), - ?assertMatch(<<"jeena">>, gen_server:call(GameVM, {eval, "hello('jeena')"})). - -stop_test() -> - GameVM = start_link(test_table), - ok = stop(GameVM). - -user_command_test() -> - GameVM = start_link(test_table), - define(GameVM, "var t = '';\nfunction userCommand(user, command, args) { t = user + command + args; }\n"), - user_command(GameVM, "'jeena", "thecommand", "theargs'"), - ?assertMatch(<<"'jeenathecommandtheargs'">>, gen_server:call(GameVM, {eval, "t;"})). - -js_erlang_test() -> - GameVM = start_link(test_table), - define(GameVM, "var t = '';\nfunction userCommand(user, command, args) { t = callErlang('erlang time') + ''; }\n"), - user_command(GameVM, "", "", ""), - {A, B, C} = erlang:time(), - T = "{" ++ integer_to_list(A) ++ ", " ++ integer_to_list(B) ++ ", " ++ integer_to_list(C) ++ "}", - ?assertMatch(T, binary_to_list(gen_server:call(GameVM, {eval, "t;"}))). - diff --git a/src/ggs_table.erl b/src/ggs_table.erl index f495a45..d6d2988 100644 --- a/src/ggs_table.erl +++ b/src/ggs_table.erl @@ -12,12 +12,13 @@ %% API -export([start_link/0, - add_player/2, - remove_player/2, - stop/1, - notify/3]). + add_player/2, + remove_player/2, + stop/1, + notify/3, + notify_all_players/2, + notify_game/3]). --include_lib("eunit/include/eunit.hrl"). %% ---------------------------------------------------------------------- % API implementation @@ -25,6 +26,7 @@ % @doc returns a new table start_link() -> {ok, Pid} = gen_server:start_link(?MODULE, [], []), + Pid. %% @private call(Pid, Msg) -> @@ -96,9 +98,10 @@ handle_cast({notify_game, Message, From}, #state { game_vm = GameVM } = State) - {noreply, State}; handle_cast({notify_all_players, Message}, #state{players = Players} = State) -> - lists:foreach(fun(P) -> - ggs_player:notify(P, "Server", Message) - end, Players), + lists:foreach( + fun(P) -> ggs_player:notify(P, "Server", Message) end, + Players + ), {noreply, State}; handle_cast(stop, State) -> @@ -120,80 +123,3 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. -%% @TODO: Please put these tests in a separate file. We can't compile this file if -%% they contain errors from switching vms -%% ---------------------------------------------------------------------- -% Tests - -%<<<<<<< HEAD -%start_link_test() -> -% Table = start_link(), -% ?assertNot(Table =:= undefined). - -%add_player_test() -> -% Table = start_link(), -% Player = test_player, -% add_player(Table, Player), -% {ok, [Player]} = gen_server:call(Table, get_player_list). - -%remove_player_test() -> -% Table = start_link(), -% Player = test_player, -% Player2 = test_player2, -% add_player(Table, Player), -% {ok, [Player]} = gen_server:call(Table, get_player_list), -% add_player(Table, Player2), -% {ok, [Player2, Player]} = gen_server:call(Table, get_player_list), -% remove_player(Table, Player), -% {ok, [Player2]} = gen_server:call(Table, get_player_list), -% remove_player(Table, Player2), -% {ok, []} = gen_server:call(Table, get_player_list). -% -%stop_test() -> -% Table = start_link(), -% ok = stop(Table). - -% @private -%notify_test() -> -% Table = start_link(), -% Player = test_player, -% Message = {server, define, "function helloWorld(x) { }"}, -% ok = notify(Table, Player, Message). -%======= -%%start_link_test() -> -% Table = start_link("123", none), -% ?assertNot(Table =:= undefined). -% -%add_player_test() -> -% Table = start_link("123", none), -% Player = test_player, -% add_player(Table, Player), -% {ok, [Player]} = gen_server:call(Table, get_player_list). - -%remove_player_test() -> -% Table = start_link("123", none), -% Player = test_player, -% Player2 = test_player2, -% add_player(Table, Player), -% {ok, [Player]} = gen_server:call(Table, get_player_list), -% add_player(Table, Player2), -% {ok, [Player2, Player]} = gen_server:call(Table, get_player_list), -% remove_player(Table, Player), -% {ok, [Player2]} = gen_server:call(Table, get_player_list), -% remove_player(Table, Player2), -% {ok, []} = gen_server:call(Table, get_player_list). -% -%stop_test() -> -% Table = start_link("123", none), -% ok = stop(Table). -% -%% @private -%notify_test() -> -% Table = start_link("123", none), -% Player = test_player, -% Message = {server, define, "function helloWorld(x) { }"}, -% ok = notify(Table, Player, Message). -%>>>>>>> jonte_rewrite - %Message2 = {game, "helloWorld", "test"}, - %ok = notify(Table, Player, Message2). - diff --git a/start b/start deleted file mode 100755 index 6de5737..0000000 --- a/start +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -erl -sname ggs -mnesia -boot start_sasl -pa erlang_js/ebin/ -pa ebin -pa src -s start_ggs diff --git a/start_test b/start_test deleted file mode 100755 index 76050c1..0000000 --- a/start_test +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -erl -boot start_sasl -pa ebin_test -pa erlang_js/ebin/ -pa ebin -pa src -eval 'ggs_coordinator_test:test().' diff --git a/start_test_shell b/start_test_shell deleted file mode 100755 index f2135c4..0000000 --- a/start_test_shell +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -erl -boot start_sasl -pa ebin_test -pa erlang_js/ebin/ -pa erlv8/ebin -pa ebin -pa src diff --git a/tests/ggs_db_test.erl b/tests/ggs_db_test.erl new file mode 100644 index 0000000..f2608f1 --- /dev/null +++ b/tests/ggs_db_test.erl @@ -0,0 +1,47 @@ +-module(ggs_db_test). +%-compile({no_auto_import,[get/1,set/2]}). +-include_lib("eunit/include/eunit.hrl"). + +%ggs_db_test_() -> +% {spawn, +% {setup, fun setup/0, fun cleanup/1,[ fun ggs_db_test/0 ]} +% }. + + +%Key should be a tuple of two elements +getItem_setItem_test() -> + ggs_db:init(), + ggs_db:setItem("dbname","nsname","keyname1","Hello"), + ggs_db:setItem("dbname","nsname","keyname2","Hello2"), + ggs_db:setItem("dbname2","nsname","keyname1","Hello3"), + ggs_db:setItem("dbname2","nsname","keyname1","Hello4"), + ggs_db:setItem("dbname3","nsname","keyname1","Hello5"), + "Hello" = ggs_db:getItem("dbname","nsname","keyname1"). + +%Test the length function of our database +length_test() -> + ggs_db:setItem(1,1,2,"112"), + ggs_db:setItem(1,2,2,"122"), + ggs_db:setItem(1,1,3,"113"), + ggs_db:setItem(1,1,4,"114"), + ?assertEqual(ggs_db:length(1,1), 3). + +%Test if we can remove correctly from the database +removeItem_test() -> + ggs_db:removeItem(1,1,4), + ?assertNot(ggs_db:getItem(1,1,4) =:= "114"). + +%Test the key function +key_test() -> + ?assert(ggs_db:key(1,1,2) =:= {1,1,2}). + +%Test the clear function(for gametoken and ns) +clear_test() -> + ggs_db:clear(1,1), + ?assert(ggs_db:length(1,1) =:= 0). + +%Test the clear function(gametoken) +clear_GameToken_test() -> + ggs_db:clear(1), + ?assert((ggs_db:length(1,1) + ggs_db:length(1,2)) =:= 0), + ggs_db:stop(). diff --git a/tests/ggs_gamevm_test.erl b/tests/ggs_gamevm_test.erl new file mode 100644 index 0000000..1689dd1 --- /dev/null +++ b/tests/ggs_gamevm_test.erl @@ -0,0 +1,31 @@ +-module(ggs_gamevm_test). +-include_lib("eunit/include/eunit.hrl"). + +start_link_test() -> + erlang_js:start(), %% @TODO: should only be done once + GameVM = start_link(test_table), + ?assertNot(GameVM =:= undefined). + +define_test() -> + GameVM = start_link(test_table), + define(GameVM, "function hello(test) { return test; }"), + ?assertMatch(<<"jeena">>, gen_server:call(GameVM, {eval, "hello('jeena')"})). + +stop_test() -> + GameVM = start_link(test_table), + ok = stop(GameVM). + +user_command_test() -> + GameVM = start_link(test_table), + define(GameVM, "var t = '';\nfunction userCommand(user, command, args) { t = user + command + args; }\n"), + user_command(GameVM, "'jeena", "thecommand", "theargs'"), + ?assertMatch(<<"'jeenathecommandtheargs'">>, gen_server:call(GameVM, {eval, "t;"})). + +js_erlang_test() -> + GameVM = start_link(test_table), + define(GameVM, "var t = '';\nfunction userCommand(user, command, args) { t = callErlang('erlang time') + ''; }\n"), + user_command(GameVM, "", "", ""), + {A, B, C} = erlang:time(), + T = "{" ++ integer_to_list(A) ++ ", " ++ integer_to_list(B) ++ ", " ++ integer_to_list(C) ++ "}", + ?assertMatch(T, binary_to_list(gen_server:call(GameVM, {eval, "t;"}))). + diff --git a/tests/ggs_player_test.erl b/tests/ggs_player_test.erl index be38135..efe7530 100644 --- a/tests/ggs_player_test.erl +++ b/tests/ggs_player_test.erl @@ -1,5 +1,5 @@ +-module(ggs_player_test). -include_lib("eunit/include/eunit.hrl"). --import(ggs_player). %% @doc start_link should always return ok for any valid socket. A valid socket %% should always return {ok, Pid} and {error, Reason} otherwise. @@ -9,9 +9,9 @@ start_link_test() -> %% @doc Given that start_link returned {ok, Player}. Notify shall always return ok and %% deliver a specified message through the socket. notify_test() -> - Player = start_link("bad arg"), + Player = ggs_player:start_link("bad arg"), Message = {"something", ""}, - Ret = ggs_player:notify(Player, self(), Message) + Ret = ggs_player:notify(Player, self(), Message), ?assertNot(ok =:= Ret). %% @doc Given that start_link returned {ok, Player}. get_token shall always return a valid @@ -22,6 +22,6 @@ get_token_test() -> %% @doc Given that start_link returned {ok, Pid}. There shouldn't be possible to %% execute this function with the same Player and Table arguments twice. stop_test() -> - Player = start_link(something), + Player = ggs_player:start_link(something), Table = test, - ok = stop(Player, Table). + ok = ggs_player:stop(Player, Table). diff --git a/tests/ggs_table_test.erl b/tests/ggs_table_test.erl new file mode 100644 index 0000000..8dbe11e --- /dev/null +++ b/tests/ggs_table_test.erl @@ -0,0 +1,38 @@ +-module(ggs_table_test). +-include_lib("eunit/include/eunit.hrl"). + +start_link_test() -> + Table = ggs_table:start_link(), + ?assertNot(Table =:= undefined). + +add_player_test() -> + Table = ggs_table:start_link(), + Player = test_player, + ggs_table:add_player(Table, Player), + {ok, [Player]} = gen_server:call(Table, get_player_list). + +remove_player_test() -> + Table = ggs_table:start_link(), + Player = test_player, + Player2 = test_player2, + ggs_table:add_player(Table, Player), + {ok, [Player]} = gen_server:call(Table, get_player_list), + ggs_table:add_player(Table, Player2), + {ok, [Player2, Player]} = gen_server:call(Table, get_player_list), + ggs_table:remove_player(Table, Player), + {ok, [Player2]} = gen_server:call(Table, get_player_list), + ggs_table:remove_player(Table, Player2), + {ok, []} = gen_server:call(Table, get_player_list). + +stop_test() -> + Table = ggs_table:start_link(), + ok = ggs_table:stop(Table). + +notify_test() -> + Table = ggs_table:start_link(), + Player = test_player, + Message = {server, define, "function helloWorld(x) { }"}, + ok = ggs_table:notify(Table, Player, Message), + Message2 = {game, "helloWorld", "test"}, + ok = ggs_table:notify(Table, Player, Message2). +