diff --git a/.gitignore b/.gitignore index f6b93e9..55bdd6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -*.swp +*.sw* *.dump *.beam Mnesia.* diff --git a/erlang_js b/erlang_js index 5350ed2..2f2785f 160000 --- a/erlang_js +++ b/erlang_js @@ -1 +1 @@ -Subproject commit 5350ed21606606dbee5ecb07e974f2abb9106270 +Subproject commit 2f2785fafb0da6db75810eb6fa97d09c58257588 diff --git a/games/GGSCalc/calc.glade b/games/GGSCalc/calc.glade new file mode 100644 index 0000000..720b7b4 --- /dev/null +++ b/games/GGSCalc/calc.glade @@ -0,0 +1,317 @@ + + + + + + + + True + + + True + True + + + + False + 0 + + + + + True + 5 + 4 + + + True + True + True + + + + + / + True + True + True + + + + 1 + 2 + + + + + * + True + True + True + + + + 2 + 3 + + + + + - + True + True + True + + + + 3 + 4 + + + + + 7 + True + True + True + + + + 1 + 2 + + + + + 8 + True + True + True + + + + 1 + 2 + 1 + 2 + + + + + 9 + True + True + True + + + + 2 + 3 + 1 + 2 + + + + + + + True + True + True + + + + 3 + 4 + 1 + 2 + + + + + 4 + True + True + True + + + + 2 + 3 + + + + + 5 + True + True + True + + + + 1 + 2 + 2 + 3 + + + + + 6 + True + True + True + + + + 2 + 3 + 2 + 3 + + + + + + True + True + True + + + + 3 + 4 + 2 + 3 + + + + + 1 + True + True + True + + + + 3 + 4 + + + + + 2 + True + True + True + + + + 1 + 2 + 3 + 4 + + + + + 3 + True + True + True + + + + 2 + 3 + 3 + 4 + + + + + = + True + True + True + + + + 3 + 4 + 3 + 4 + + + + + 0 + True + True + True + + + + 4 + 5 + + + + + True + True + True + + + 1 + 2 + 4 + 5 + + + + + True + True + True + + + 2 + 3 + 4 + 5 + + + + + + True + True + True + + + + 3 + 4 + 4 + 5 + + + + + 1 + + + + + True + 2 + + + False + 2 + + + + + + diff --git a/games/GGSCalc/calc.py b/games/GGSCalc/calc.py new file mode 100644 index 0000000..8468ceb --- /dev/null +++ b/games/GGSCalc/calc.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python + +import sys, socket +try: + import pygtk + pygtk.require("2.16") +except: + pass +try: + import gtk + import gtk.glade +except: + sys.exit(1) + +class GGSCalc: + + def __init__(self): + #Set the Glade file + self.gladefile = "calc.glade" + self.wTree = gtk.glade.XML(self.gladefile, "window1") + + #Create our dictionay and connect it + dic = {"on_mainWindow_destroy" : gtk.main_quit + , "on_btn0_clicked" : lambda x: self.OnBtnClick(0) + , "on_btn1_clicked" : lambda x: self.OnBtnClick(1) + , "on_btn2_clicked" : lambda x: self.OnBtnClick(2) + , "on_btn3_clicked" : lambda x: self.OnBtnClick(3) + , "on_btn4_clicked" : lambda x: self.OnBtnClick(4) + , "on_btn5_clicked" : lambda x: self.OnBtnClick(5) + , "on_btn6_clicked" : lambda x: self.OnBtnClick(6) + , "on_btn7_clicked" : lambda x: self.OnBtnClick(7) + , "on_btn8_clicked" : lambda x: self.OnBtnClick(8) + , "on_btn9_clicked" : lambda x: self.OnBtnClick(9) + , "on_btnDiv_clicked" : lambda x: self.OnBtnClick("/") + , "on_btnMul_clicked" : lambda x: self.OnBtnClick("*") + , "on_btnMin_clicked" : lambda x: self.OnBtnClick("-") + , "on_btnPlus_clicked" : lambda x: self.OnBtnClick("+") + , "on_btnEq_clicked" : lambda x: self.calc() + , "on_btnDel_clicked" : lambda x: self.OnBtnClick("Del") + , "on_btnConnect_clicked" : lambda x: self.connect() + } + + for i in range(0,9): + dic + self.wTree.signal_autoconnect(dic) + + self.wTree.get_widget("window1").show() + self.setStatus("Not connected") + + def setStatus(self, msg): + self.wTree.get_widget("statusbar").push(0, msg) + + def calc(self): + exp = self.wTree.get_widget("txtCalc").get_text() + self.s.send("Server-Command: call\n"+ + "Token: %s\n" % self.token + + "Content-Type: text\n"+ + "Content-Length: %s\n" % len(exp)+ + "\n"+ + exp) + fs = self.s.makefile() + self.wTree.get_widget("txtCalc").set_text(fs.readline().split(" ")[1]) + + + def connect(self): + print "Connecting" + self.setStatus("Connecting") + HOST = 'localhost' # The remote host + PORT = 9000 # The same port as used by the server + self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.s.connect((HOST, PORT)) + self.s.send("Server-Command: hello\n"+ + "Content-Type: text\n"+ + "Content-Length: 0\n"+ + "\n") + fs = self.s.makefile() + self.token = fs.readline().split(" ")[0] + self.setStatus("Connected!") + + def OnBtnClick(self, btn): + calcTxt = self.wTree.get_widget("txtCalc") + t = calcTxt.get_text() + if btn == "+": + t += "+" + elif btn == "-": + t += "-" + elif btn == "/": + t += "/" + elif btn == "=": + t += "=" + elif btn == "*": + t += "*" + elif btn == "Del": + t = t[:-1] + else: + t += str("\""+str(btn)+"\"") + calcTxt.set_text(t) + +if __name__ == "__main__": + calc = GGSCalc() + gtk.main() diff --git a/games/GGSChat/calc.glade b/games/GGSChat/calc.glade new file mode 100644 index 0000000..720b7b4 --- /dev/null +++ b/games/GGSChat/calc.glade @@ -0,0 +1,317 @@ + + + + + + + + True + + + True + True + + + + False + 0 + + + + + True + 5 + 4 + + + True + True + True + + + + + / + True + True + True + + + + 1 + 2 + + + + + * + True + True + True + + + + 2 + 3 + + + + + - + True + True + True + + + + 3 + 4 + + + + + 7 + True + True + True + + + + 1 + 2 + + + + + 8 + True + True + True + + + + 1 + 2 + 1 + 2 + + + + + 9 + True + True + True + + + + 2 + 3 + 1 + 2 + + + + + + + True + True + True + + + + 3 + 4 + 1 + 2 + + + + + 4 + True + True + True + + + + 2 + 3 + + + + + 5 + True + True + True + + + + 1 + 2 + 2 + 3 + + + + + 6 + True + True + True + + + + 2 + 3 + 2 + 3 + + + + + + True + True + True + + + + 3 + 4 + 2 + 3 + + + + + 1 + True + True + True + + + + 3 + 4 + + + + + 2 + True + True + True + + + + 1 + 2 + 3 + 4 + + + + + 3 + True + True + True + + + + 2 + 3 + 3 + 4 + + + + + = + True + True + True + + + + 3 + 4 + 3 + 4 + + + + + 0 + True + True + True + + + + 4 + 5 + + + + + True + True + True + + + 1 + 2 + 4 + 5 + + + + + True + True + True + + + 2 + 3 + 4 + 5 + + + + + + True + True + True + + + + 3 + 4 + 4 + 5 + + + + + 1 + + + + + True + 2 + + + False + 2 + + + + + + diff --git a/games/GGSChat/chat.py b/games/GGSChat/chat.py new file mode 100644 index 0000000..17c30ea --- /dev/null +++ b/games/GGSChat/chat.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python + +import sys, socket, thread, gobject, getpass +try: + import pygtk + pygtk.require("2.16") +except: + pass +try: + import gtk + import gtk.glade +except: + sys.exit(1) + +class GGSChat: + + def __init__(self,host, port): + #Set the Glade file + self.gladefile = "ggschat.glade" + self.wTree = gtk.glade.XML(self.gladefile, "window1") + + self.setStatus("Not connected") + self.connect(host, port) + thread.start_new_thread(self.listenChat, ()) + #Create our dictionay and connect it + dic = {"on_window1_destroy_event" : gtk.main_quit + , "on_sendButton_clicked" : lambda x: self.chat() + , "on_entry_activate" : lambda x : self.chat() + , "on_chatBox_focus" : lambda x, y: self.wTree.get_widget("entry").grab_focus() + } + + self.wTree.signal_autoconnect(dic) + + self.wTree.get_widget("nickBox").set_text(getpass.getuser()) + self.wTree.get_widget("window1").show() + self.wTree.get_widget("entry").grab_focus() + + def setStatus(self, msg): + self.wTree.get_widget("statusbar").push(0, msg) + + def chat(self): + exp = self.wTree.get_widget("entry").get_text() + nick = self.wTree.get_widget("nickBox").get_text() + if exp[0] == "/": + cmdStr = exp[1:].split(" ") + cmd = cmdStr[0] + params = ' '.join(cmdStr[1:]) + self.s.send("Game-Command: %s\n" % exp[1:] + + "Token: %s\n" % self.token + + "Content-Type: text\n" + + "Content-Length: %s\n" % len(params)+ + "\n"+ + params) + else: + exp = "<%s> %s" % (nick, exp) + self.s.send("Game-Command: chat\n"+ + "Token: %s\n" % self.token + + "Content-Type: text\n"+ + "Content-Length: %s\n" % (len(exp))+ + "\n"+ + exp+"\n") + self.wTree.get_widget("entry").set_text("") + #self.listenChat() + + + def connect(self, host,port): + print "Connecting" + self.setStatus("Connecting") + HOST = host # The remote host + PORT = port # The same port as used by the server + self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.s.connect((HOST, PORT)) + self.token = self.s.recv(1024) + self.setStatus("Connected!") + + def listenChat(self): + print "listening" + fs = self.s.makefile() + while True: + line = fs.readline() + print "Received: ", line + gobject.idle_add(self.updateChatText, line) + + def updateChatText(self, text): + self.wTree.get_widget("chatBox").get_buffer().insert_at_cursor(text) +if __name__ == "__main__": + host = "localhost" + port = 9000 + if len(sys.argv) >= 2: + host = sys.argv[1] + port = int(sys.argv[2]) + chat = GGSChat(host, port) + gobject.threads_init() + gtk.main() diff --git a/games/GGSChat/ggschat.glade b/games/GGSChat/ggschat.glade new file mode 100644 index 0000000..ac4e9cb --- /dev/null +++ b/games/GGSChat/ggschat.glade @@ -0,0 +1,92 @@ + + + + + + 500 + 500 + + + + True + + + True + + + True + True + False + + + + 0 + + + + + 0 + + + + + True + + + True + True + + 10 + Anonymous + + + False + 0 + + + + + True + True + + + + + 1 + + + + + Chat! + True + True + True + + + + False + False + 2 + + + + + False + False + 1 + + + + + True + 2 + + + False + 2 + + + + + + diff --git a/python_client b/python_client index e265120..50e8dbd 100755 --- a/python_client +++ b/python_client @@ -7,54 +7,61 @@ PORT = int(sys.argv[1]) # The same port as used by the server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((HOST, PORT)) -# Say hello +# Define ourselves a function! +token = s.recv(1024) -print "Saying hello to server" +#print "Defining a function called myFun" +#s.send( +#"Token: %s\n\ +#Server-Command: define\n\ +#Content-Type: text\n\ +#Content-Length: 49\n\ +#\n\ +#function myFun() {return 'Hello World!' ;}" % token) +#fs = s.makefile() +#data = fs.readline() +#print "Token:", token +#print "Data: ", ' '.join(data.split(" ")[1:]) + +# Call that function! +fs = s.makefile() +print "Token: ", token s.send( -"Server-Command: hello\n\ +"Token: %s\n\ +Game-Command: greet\n\ Content-Type: text\n\ Content-Length: 0\n\ \n\ -") -fs = s.makefile() -data = fs.readline() -token = data.split(" ")[0] -print "Token:", token -print "Data: ", ' '.join(data.split(" ")[1:]) +" % token) +time.sleep(1) -# Define ourselves a function! - -print "Defining a function called myFun" s.send( "Token: %s\n\ -Server-Command: define\n\ +Game-Command: uname\n\ Content-Type: text\n\ -Content-Length: 49\n\ +Content-Length: 0\n\ \n\ -function myFun() {return 'Hello World!' ;}" % token) -fs = s.makefile() -data = fs.readline() -print "Token:", token -print "Data: ", ' '.join(data.split(" ")[1:]) +" % token) +time.sleep(1) -# Call that function! - -print "Calling myFun" s.send( "Token: %s\n\ -Server-Command: call\n\ +Game-Command: chat\n\ Content-Type: text\n\ -Content-Length: 6\n\ +Content-Length: 23\n\ \n\ -myFun" % token) -fs = s.makefile() -data = fs.readline() -print "Token:", token -print "Data: ", ' '.join(data.split(" ")[1:]) +Hello guys, what's up?\n" % token) +time.sleep(1) + + +while True: + data = fs.readline() + print "Data: ", data s.close() -""" +time.sleep(2) + HOST = 'localhost' # The remote host PORT = int(sys.argv[1]) # The same port as used by the server s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -75,4 +82,3 @@ print "Token:", token print "Data: ", ' '.join(data.split(" ")[1:]) s.close() -""" diff --git a/python_client_reconnect b/python_client_reconnect new file mode 100755 index 0000000..2a41b70 --- /dev/null +++ b/python_client_reconnect @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +import sys, time, socket + +HOST = 'localhost' # The remote host +PORT = int(sys.argv[1]) # The same port as used by the server +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.connect((HOST, PORT)) +# Call that function! + +print "Calling myFun" +s.send( +"Token: %s\n\ +Server-Command: call\n\ +Content-Type: text\n\ +Content-Length: 6\n\ +\n\ +myFun" % raw_input("Token >> ")) +fs = s.makefile() +data = fs.readline() +print "Data: ", ' '.join(data.split(" ")[1:]) + +s.close() diff --git a/src/ggs_coordinator.erl b/src/ggs_coordinator.erl index 83f7e14..48b4639 100644 --- a/src/ggs_coordinator.erl +++ b/src/ggs_coordinator.erl @@ -28,15 +28,6 @@ stop(Reason) -> %% @doc Joins table with specified token, returns {error, no_such_table} %% if the specified table token does not exist -join_table(Token) -> - gen_server:call(ggs_coordinator, {join_table, Token}). - -%% @doc Create a new table, return {error, Reason} or {ok, TableToken} -create_table(Params) -> - gen_server:call(ggs_coordinator, {create_table, Params}). - -%% @doc This is the first function run by a newly created players. -%% Generates a unique token that we use to identify the player. join_lobby() -> gen_server:call(ggs_coordinator, join_lobby). @@ -50,6 +41,7 @@ respawn_table(_Token) -> %% @doc Removes a player from coordinator. remove_player(_From, _Player) -> + %gen_server:cast(ggs_coordinator, {remove_player, Player}). ggs_logger:not_implemented(). %% gen_server callbacks diff --git a/src/ggs_gamevm_e.erl b/src/ggs_gamevm_e.erl index 6ca4e1b..3cc6b17 100644 --- a/src/ggs_gamevm_e.erl +++ b/src/ggs_gamevm_e.erl @@ -33,9 +33,28 @@ user_command(GameVM, Player, Command, Args) -> loop(Table) -> receive {define, _SourceCode} -> + io:format("GameVM_e can't define functions, sorry!~n"), loop(Table); - {user_command, _User, Command, _Args, _From, _Ref} -> - io:format("GameVM received a message~n"), - ggs_table:notify_all_players(Table, Command), + {user_command, Player, Command, Args, _From, _Ref} -> + erlang:display(Command), + do_stuff(Command, Args, Player, Table), loop(Table) end. + +do_stuff(Command, Args, Player, Table) -> + case Command of + "greet" -> + ggs_player:notify(Player, server, "Hello there!\n"); + "chat" -> + ggs_table:notify_all_players(Table, Args ++ "\n"); + "uname" -> + Uname = os:cmd("uname -a"), + ggs_player:notify(Player, server, Uname); + "lusers" -> + {ok, Players} = ggs_table:get_player_list(Table), + ggs_player:notify(Player, server,io_lib:format("~p\n",[Players])); + "nick" -> + io:format("Changing nickname of ~p to ~p.", [Player, Args]); + _Other -> + ggs_player:notify(Player, server, "I don't know that command..\n") + end. diff --git a/src/ggs_player.erl b/src/ggs_player.erl index 27c73d0..0211f3f 100644 --- a/src/ggs_player.erl +++ b/src/ggs_player.erl @@ -26,10 +26,12 @@ start_link(Socket) -> TableStatus = ggs_coordinator:join_table(1337), case TableStatus of {ok, Table} -> + notify(self(), self(), Token), loop(#pl_state{socket = Socket, token = Token, table = Table}); {error, no_such_table} -> ggs_coordinator:create_table({force, 1337}), {ok, Table} = ggs_coordinator:join_table(1337), + notify(self(), self(), Token), loop(#pl_state{socket = Socket, token = Token, table = Table}) end. @@ -54,13 +56,28 @@ stop(_Player,_Table) -> %% Internals -loop(#pl_state{token = Token, socket = Socket, table = Table} = State) -> +loop(#pl_state{token = _Token, socket = Socket, table = Table} = State) -> receive {tcp, Socket, Data} -> % Just echo for now.. - io:format("Notifying table..~n"), - ggs_table:notify_game(Table, Token, Data), + io:format("Parsing via protocol module..~n"), + Parsed = ggs_protocol:parse(Data), + self() ! Parsed, loop(State); {notify, _From, Message} -> gen_tcp:send(Socket, Message), - loop(State) + loop(State); + % Below are messages generated by the parser + {game_cmd,Cmd, _Headers, Data} -> + ggs_table:notify(Table, self(), {game, Cmd, Data}), + loop(State); + {srv_cmd,"define", _Headers, Data} -> + ggs_table:notify(Table, self(), {server, define, Data}), + loop(State); + {tcp_closed, _Socket} -> + io:format("Client disconnected, but THIS IS NOT SUPPORTED YET!~n"), + loop(State); + Other -> + io:format("Got UNKNOWN message: "), + erlang:display(Other), + io:format("~n") end. diff --git a/src/ggs_protocol.erl b/src/ggs_protocol.erl new file mode 100644 index 0000000..3dc88f9 --- /dev/null +++ b/src/ggs_protocol.erl @@ -0,0 +1,61 @@ +-module(ggs_protocol). +-export([parse/1, getToken/1]). + +%% API Functions +parse(Data) -> + Parsed = do_parse(Data, []), + prettify(Parsed). + +getToken(Parsed) -> + case lists:keyfind(token, 1, Parsed) of + {_, Value} -> + Value; + false -> + false + end. + +%% Internal helpers +do_parse(Data, ParsedMessage) -> + NewLinePos = string:chr(Data, $\n), + Line = string:substr(Data, 1, NewLinePos-1), + Tokens = re:split(Line, ": ", [{return, list}]), + case handle(Tokens) of + {Command, more} -> + do_parse(string:substr(Data, NewLinePos+1), ParsedMessage ++ [Command]); + {separator, data_next} -> + {_, Value} = lists:keyfind(content_len, 1, ParsedMessage), + {ContentLength, []} = string:to_integer(Value), + {data, ArgumentData} = handle_data(string:substr(Data, NewLinePos+1), ContentLength), + {ParsedMessage, ArgumentData} + end. + +handle([[]]) -> + {separator, data_next}; +handle(["Server-Command", Param]) -> + {{srv_cmd, Param}, more}; +handle(["Game-Command", Param]) -> + {{game_cmd, Param}, more}; +handle(["Content-Length", Param]) -> + {{content_len, Param}, more}; +handle(["Token", Param]) -> + {{token, Param}, more}; +handle(["Content-Type", Param]) -> + {{content_type, Param}, more}. + +handle_data(Data, Length) -> + {data, string:substr(Data,1,Length)}. + + +prettify({Args, Data}) -> + case lists:keyfind(srv_cmd, 1, Args) of + {_, Value} -> + {srv_cmd, Value, Args, Data}; + _Other -> + case lists:keyfind(game_cmd, 1, Args) of + {_, Value} -> + {game_cmd, Value, Args, Data}; + _ -> + ok + end + end. + diff --git a/src/ggs_table.erl b/src/ggs_table.erl index b696688..d6d2988 100644 --- a/src/ggs_table.erl +++ b/src/ggs_table.erl @@ -5,7 +5,8 @@ %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). + terminate/2, code_change/3, notify_all_players/2, notify_game/3, + get_player_list/1]). -record(state, { players, game_vm } ). @@ -39,6 +40,10 @@ add_player(Table, Player) -> remove_player(Table, Player) -> call(Table, {remove_player, Player}). +%% @doc Get a list of all player processes attached to this table +get_player_list(Table) -> + gen_server:call(Table, get_player_list). + % @doc stops the table process stop(Table) -> gen_server:cast(Table, stop). @@ -51,6 +56,8 @@ notify_all_players(Table, Message) -> gen_server:cast(Table, {notify_all_players, Message}). notify_game(Table, From, Message) -> + erlang:display(Table), + io:format("~n"), gen_server:cast(Table, {notify_game, Message, From}). %% ---------------------------------------------------------------------- @@ -79,15 +86,14 @@ handle_call(Msg, _From, State) -> %% @private handle_cast({notify, Player, Message}, #state { game_vm = GameVM } = State) -> case Message of - {server, define, Args} -> - ggs_gamevm_e:define(GameVM, Args); - {game, Command, Args} -> - ggs_gamevm_e:user_command(GameVM, Player, Command, Args) + {server, define, Args} -> + ggs_gamevm_e:define(GameVM, Args); + {game, Command, Args} -> + ggs_gamevm_e:user_command(GameVM, Player, Command, Args) end, {noreply, State}; handle_cast({notify_game, Message, From}, #state { game_vm = GameVM } = State) -> - io:format("notify_game message received~n"), ggs_gamevm_e:user_command(GameVM, From, Message, ""), {noreply, State};