diff --git a/.gitignore b/.gitignore
index d176978..55bdd6f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
-*.swp
+*.sw*
*.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/ebin/ggs.app b/ebin/ggs.app
index 3315864..b3ea744 100644
--- a/ebin/ggs.app
+++ b/ebin/ggs.app
@@ -3,7 +3,8 @@
{vsn, "0.1.0"},
{modules, [
ggs_app,
- ggs_sup
+ ggs_sup,
+ ggs_dispatcher
]},
{registered, [ggs_sup]},
{applications, [kernel, stdlib]},
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/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/mnesia/.gamedb.erl.swp b/mnesia/.gamedb.erl.swp
deleted file mode 100644
index 469b1f8..0000000
Binary files a/mnesia/.gamedb.erl.swp and /dev/null differ
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/python_client b/python_client
index 3db0751..50e8dbd 100755
--- a/python_client
+++ b/python_client
@@ -7,50 +7,56 @@ 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()
diff --git a/src/.ggs_connection.erl.swp b/src/.ggs_connection.erl.swp
deleted file mode 100644
index 0c009f8..0000000
Binary files a/src/.ggs_connection.erl.swp and /dev/null differ
diff --git a/src/.ggs_server.erl.swo b/src/.ggs_server.erl.swo
deleted file mode 100644
index 3048659..0000000
Binary files a/src/.ggs_server.erl.swo and /dev/null differ
diff --git a/src/ggs_backup.erl b/src/ggs_backup.erl
deleted file mode 100644
index 30c80a2..0000000
--- a/src/ggs_backup.erl
+++ /dev/null
@@ -1,41 +0,0 @@
--module(ggs_backup).
--behaviour(gen_server).
-
-%% API
--export([start_link/0 ]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2,
- handle_info/2, terminate/2, code_change/3]).
-
-
--define(SERVER, ?MODULE).
-
--record(state, {port, lsock, client_vm_map = []}).
-
-start_link() ->
- gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
-
-init([]) ->
- {ok, #state{port = -1, lsock = -1, client_vm_map = -1}, 0}.
-
-handle_call(get_backup, _From, State) ->
- BackedUpState = case State of
- #state{port = -1, lsock = -1, client_vm_map = -1} ->
- not_initialized;
- Other ->
- Other
- end,
- {reply, {backup_state, BackedUpState}, State}.
-
-handle_cast({set_backup, NewState}, _State) ->
- {noreply, NewState}.
-
-handle_info(_Msg, State) ->
- {noreply, State}.
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-terminate(_Reason, _State) ->
- ok.
diff --git a/src/ggs_coordinator.erl b/src/ggs_coordinator.erl
new file mode 100644
index 0000000..48b4639
--- /dev/null
+++ b/src/ggs_coordinator.erl
@@ -0,0 +1,92 @@
+-module(ggs_coordinator).
+
+%% API Exports
+-export([start_link/0, stop/1, join_table/1, create_table/1, join_lobby/0,
+ respawn_player/2, respawn_table/1, remove_player/2]).
+
+%% gen_server callback exports
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
+ code_change/3]).
+-define(SERVER, ?MODULE).
+
+-record(co_state,
+ {players = [], % List of all player processes
+ player_table_map = [], % Players <-> Table map
+ table_state_map = [],
+ tables = []}). % Table <-> Table state map
+
+%% @doc This module act as "the man in the middle".
+%% Creates the starting connection between table and players.
+
+%% @doc Starts the coordinator process.
+start_link() ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
+
+%% @doc Terminates the coordinator process.
+stop(Reason) ->
+ gen_server:cast(ggs_coordinator, {stop, Reason}).
+
+%% @doc Joins table with specified token, returns {error, no_such_table}
+%% if the specified table token does not exist
+join_lobby() ->
+ gen_server:call(ggs_coordinator, join_lobby).
+
+%% @doc Act as a supervisor to player and respawns player when it gets bad data.
+respawn_player(_Player, _Socket) ->
+ ggs_logger:not_implemented().
+
+%% @doc Act as a supervisor to table and respawns table when it gets bad data.
+respawn_table(_Token) ->
+ ggs_logger:not_implemented().
+
+%% @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
+
+init([]) ->
+ {ok, #co_state{}}.
+
+handle_call(join_lobby, _From, State) ->
+ Token = helpers:get_new_token(),
+ {reply, {ok, Token}, State};
+
+handle_call({join_table, Table}, From, State) ->
+ {FromPlayer, _Ref} = From,
+ Tables = State#co_state.tables,
+ case lists:keyfind(Table, 1, Tables) of
+ {_TableID, TablePID} ->
+ ggs_table:add_player(TablePID, FromPlayer),
+ {reply, {ok, TablePID}, State};
+ false ->
+ {reply, {error, no_such_table}, State}
+ end;
+
+handle_call({create_table, {force, TableID}}, From, State) ->
+ TableIDMap = State#co_state.player_table_map,
+ Tables = State#co_state.tables,
+ NewTableProc = ggs_table:start_link(),
+ {reply, {ok, TableID}, State#co_state{
+ player_table_map = [{From, TableID} | TableIDMap],
+ tables = [{TableID, NewTableProc} | Tables]
+ }};
+
+handle_call(_Message, _From, State) ->
+ {noreply, State}.
+
+handle_cast({stop, _Reason}, _State) ->
+ {stop, normal, state};
+
+handle_cast(_Message, State) ->
+ {noreply, State}.
+
+handle_info(_Message, State) ->
+ {noreply, State}.
+
+terminate(normal, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, 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_dispatcher.erl b/src/ggs_dispatcher.erl
new file mode 100644
index 0000000..11dd729
--- /dev/null
+++ b/src/ggs_dispatcher.erl
@@ -0,0 +1,68 @@
+-module(ggs_dispatcher).
+
+-behaviour(gen_server).
+
+%% API Exports
+-export([start_link/1, stop/1]).
+
+%% gen_server callback exports
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
+ code_change/3]).
+
+-define(SERVER, ?MODULE).
+
+
+%% @doc This module is the entry-point for clients connecting to GGS. This is
+%% the module responsible for:
+%% * Greeting a connecting client, and associating a socket for it
+%% * Spawning a ggs_player for the connecting client, passing the socket
+
+%% @doc Starts a new dispatcher with the specified port. Registers this
+%% dispatcher under the name "ggs_dispatcher". The pid of the dispatcher
+%% is returned.
+%% @spec start_link(Port) -> Pid
+%% Port = Integer
+%% Pid = #
+start_link(Port) ->
+ gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
+
+%% @doc Stops the dispatcher with the specified reason.
+%% @spec stop(Reason) -> ok.
+%% Reason = String
+stop(_Reason) -> ggs_logger:not_implemented().
+
+%% gen_server callbacks
+
+%% @doc Initiate the dispatcher. This is called from gen_server
+init([Port]) ->
+ {ok, LSock} = gen_tcp:listen(Port, [{active, true},
+ {reuseaddr, true}]),
+ {ok, LSock, 0}.
+
+handle_call(_Message, _From, State) ->
+ {noreply, State}.
+
+handle_cast(_Message, State) ->
+ {noreply, State}.
+
+handle_info({tcp, _Socket, _RawData}, State) ->
+ io:format("Got connect request!~n"),
+ {noreply, State};
+
+handle_info({tcp_closed, Socket}, State) ->
+ gen_tcp:close(Socket),
+ {stop, "Client closed socket", State};
+
+%% @doc This is our function for accepting connections. When a client connects,
+%% it will immediately time out due to timing settings set in init and here,
+%% and when it does, we accept the connection.
+handle_info(timeout, LSock) ->
+ {ok, Sock} = gen_tcp:accept(LSock),
+ spawn(ggs_player, start_link, [Sock]),
+ {noreply, LSock, 0}.
+
+terminate(normal, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
diff --git a/src/ggs_gamevm.erl b/src/ggs_gamevm.erl
new file mode 100644
index 0000000..babee27
--- /dev/null
+++ b/src/ggs_gamevm.erl
@@ -0,0 +1,99 @@
+%% @doc This module is responsible for running the game VM:s. You can issue
+%% commands to a vm using this module.
+
+-module(ggs_gamevm).
+-behaviour(gen_server).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
+
+-record(state, { port, table } ).
+
+%% API
+-export([start_link/1, define/2, user_command/4, stop/1, call_js/2]).
+
+-include_lib("eunit/include/eunit.hrl").
+
+
+%% ----------------------------------------------------------------------
+% API implementation
+
+%% @doc Create a new VM process. The process ID is returned and can be used
+%% with for example the define method of this module.
+start_link(Table) ->
+ erlang_js:start(), %% @TODO: should only be done once
+ {ok, Pid} = gen_server:start_link(?MODULE, [Table], []),
+ Pid.
+
+%% @doc Define some new code on the specified VM, returns the atom ok.
+define(GameVM, SourceCode) ->
+ gen_server:cast(GameVM, {define, SourceCode}).
+
+%% @doc Execute a user command on the specified VM. This function is
+%% asynchronous, and returns ok.
+%% @spec user_command(GameVM, User, Command, Args) -> ok
+%% GameVM = process IS of VM
+%% Player = the player running the command
+%% Command = a game command to run
+%% Args = arguments for the Command parameter
+user_command(GameVM, Player, Command, Args) ->
+ gen_server:cast(GameVM, {user_command, Player, Command, Args}).
+
+%% @private
+% only for tests
+call_js(GameVM, SourceCode) ->
+ gen_server:call(GameVM, {eval, SourceCode}).
+
+% @doc stops the gamevm process
+stop(GameVM) ->
+ gen_server:cast(GameVM, stop).
+
+
+%% ----------------------------------------------------------------------
+
+%% @private
+init([Table]) ->
+ process_flag(trap_exit, true),
+ {ok, Port} = js_driver:new(),
+ %% @TODO: add here default JS API instead
+ {ok, #state { port = Port, table = Table }}.
+
+%% private
+% only needed for the tests
+handle_call({eval, SourceCode}, _From, #state { port = Port } = State) ->
+ {ok, Ret} = js:eval(Port, list_to_binary(SourceCode)),
+ {reply, Ret, State}.
+
+%% @private
+handle_cast({define, SourceCode}, #state { port = Port } = State) ->
+ ok = js:define(Port, list_to_binary(SourceCode)),
+ {noreply, State};
+handle_cast({user_command, Player, Command, Args}, #state { port = Port } = State) ->
+ Arguments = string:concat("'", string:concat(
+ string:join([js_escape(Player), js_escape(Command), js_escape(Args)], "','"), "'")),
+ Js = list_to_binary(string:concat(string:concat("userCommand(", Arguments), ");")),
+ js_driver:define_js(Port, Js),
+ {noreply, State};
+handle_cast(stop, State) ->
+ {stop, normal, State};
+handle_cast(Msg, S) ->
+ error_logger:error_report([unknown_msg, Msg]),
+ {noreply, S}.
+
+%% @private
+handle_info(Msg, S) ->
+ error_logger:error_report([unknown_msg, Msg]),
+ {noreply, S}.
+
+%% @private
+terminate(_Reason, _State) ->
+ ok.
+
+%% @private
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+js_escape(S) ->
+ lists:flatmap(fun($\') -> [$\\, $\']; (X) -> [X] end, S).
+
diff --git a/src/ggs_gamevm_e.erl b/src/ggs_gamevm_e.erl
new file mode 100644
index 0000000..3cc6b17
--- /dev/null
+++ b/src/ggs_gamevm_e.erl
@@ -0,0 +1,60 @@
+-module(ggs_gamevm_e).
+-export([start_link/1, define/2, user_command/4]).
+%% @doc This module is responsible for running the game VM:s. You can issue
+%% commands to a vm using this module.
+
+%% @doc Create a new VM process. The process ID is returned and can be used
+%% with for example the define method of this module.
+start_link(Table) ->
+ PortPid = spawn( fun() ->
+ loop(Table)
+ end ),
+ PortPid.
+
+%% @doc Define some new code on the specified VM, returns the atom ok.
+define(GameVM, SourceCode) ->
+ GameVM ! {define,SourceCode},
+ ok.
+
+%% @doc Execute a user command on the specified VM. This function is
+%% asynchronous, and returns ok.
+%% @spec user_command(GameVM, User, Command, Args) -> ok
+%% GameVM = process IS of VM
+%% Player = the player running the command
+%% Command = a game command to run
+%% Args = arguments for the Command parameter
+user_command(GameVM, Player, Command, Args) ->
+ Ref = make_ref(),
+ GameVM ! {user_command, Player, Command, Args, self(), Ref},
+ ok.
+
+%% Helper functions
+
+loop(Table) ->
+ receive
+ {define, _SourceCode} ->
+ io:format("GameVM_e can't define functions, sorry!~n"),
+ loop(Table);
+ {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_logger.erl b/src/ggs_logger.erl
new file mode 100644
index 0000000..49f5abc
--- /dev/null
+++ b/src/ggs_logger.erl
@@ -0,0 +1,8 @@
+-module(ggs_logger).
+-export([not_implemented/0, log/2]).
+
+not_implemented() ->
+ exit(not_implemented).
+
+log(Format, Args) ->
+ error_logger:info_msg(Format, Args).
diff --git a/src/ggs_mnesia_controller_server.erl b/src/ggs_mnesia_controller_server.erl
deleted file mode 100644
index c1f8a10..0000000
--- a/src/ggs_mnesia_controller_server.erl
+++ /dev/null
@@ -1,68 +0,0 @@
--module(ggs_mnesia_controller_server).
--behaviour(gen_server).
-
-%% API
--export([start_link/0,
- stop/0
- ]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2,
- handle_info/2, terminate/2, code_change/3]).
-
-
--define(SERVER, ?MODULE).
-
--record(state, {}).
-
-%%%====================================================
-%%% API
-%%%====================================================
-
-%%-----------------------------------------------------
-%% @doc Starts the server
-%% @end
-%%-----------------------------------------------------
-start_link() ->
- gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
-
-%%-----------------------------------------------------
-%% @doc Stops the server.
-%% @spec stop() -> ok
-%% @end
-%%-----------------------------------------------------
-stop() ->
- gen_server:cast(?SERVER, stop).
-
-%%-----------------------------------------------------
-%% gen_server callbacks
-%%-----------------------------------------------------
-
-init([]) ->
- mnesia:create_schema([node()]),
- mnesia:start(),
- {ok, {}, 0}.
-
-handle_cast(a, State) ->
- {noreply, State}.
-
-% Request a value from the Mnesia database
-handle_call({getValue, _Key},_From,State) ->
- {reply,value_of_key_requested_goes_here, State};
-
-% Set a value in the Mnesia database
-handle_call({setValue, _Key, Value},_From,State) ->
- {reply,value_set_or_updated, State}.
-
-handle_info(timeout, State) ->
- {noreply, State}.
-
-terminate(_Reason, _State) ->
- ok.
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%-----------------------------------------------------
-%% Internal functions
-%%-----------------------------------------------------
diff --git a/src/ggs_network.erl b/src/ggs_network.erl
deleted file mode 100644
index be1b9d1..0000000
--- a/src/ggs_network.erl
+++ /dev/null
@@ -1,170 +0,0 @@
-%%%----------------------------------------------------
-%%% @author Jonatan Plsson
-%%% @copyright 2010 Jonatan Plsson
-%%% @doc RPC over TCP server
-%%% @end
-%%%----------------------------------------------------
-%%% @author Mattias Pettersson
-%%% @doc Socket module for GGS
-%%% @end
-%%%----------------------------------------------------
-
-
--module(ggs_network).
-
--behaviour(gen_server).
-
-%define
--define(SERVER, ?MODULE).
--define(DEFAULT_PORT, 1055).
-
-
-% export
--export([start_link/0,start_link/1]).
--export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
-%-export([get_count/1,crash/0,vms/0,hello/0,echo/0]).
--export([get_count/1]).
--export([send/3, send/4]).
--export([stop/0]).
-
-%% gen_server callbacks
--export([terminate/2, code_change/3]).
-
-%state
--record(state, {port, lsock, client_vm_map = []}).
-
-
-%%-----------------------------------------------------
-%% @doc Starts gen_server
-%% @end
-%%-----------------------------------------------------
-start_link() ->
- start_link(?DEFAULT_PORT).
-
-start_link(Port) ->
- process_flag(trap_exit, true),
- gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
-
-%%-----------------------------------------------------
-%% Creation
-%%-----------------------------------------------------
-init([Port]) ->
- {ok, LSock} = gen_tcp:listen(Port, [{active, true},
- {reuseaddr, true}]),
- {ok, #state{port = Port, lsock = LSock}, 0}.
-
-%%-----------------------------------------------------
-%% @doc Fetches the number of requests made to this server
-%% @spec get_count() -> {ok, Count}
-%% where
-%% Count = integer()
-%% @end
-%%-----------------------------------------------------
-get_count(get_count) ->
- gen_server:call(?SERVER, get_count).
-
-%crash() -> gen_server:call(?SERVER, crash).
-%vms() -> gen_server:call(?SERVER, vms).
-%hello() -> gen_server:call(?SERVER, hello).
-%echo() -> gen_server:call(?SERVER, {echo, RefID, _, MSG}).
-
-
-
-
-%%-----------------------------------------------------
-%% @doc Stops the server.
-%% @spec stop() -> ok
-%% @end
-%%-----------------------------------------------------
-stop() ->
- gen_server:cast(?SERVER, stop).
-
-%%-----------------------------------------------------
-%% Handlers
-%%-----------------------------------------------------
-handle_call(get_count, _From, State) ->
- {reply, {ok, State#state.client_vm_map}, State}.
-
-handle_cast(stop, State) ->
- {stop, normal, State}.
-
-handle_info({tcp, Socket, RawData}, State) -> %parameters coming from gen_server
- NewState = do_JSCall(Socket, RawData, State), %TODO
- OldMap = State#state.client_vm_map,
- io:format("Old map: ~p NewState: ~p~n", [OldMap, NewState]),
- {noreply, State#state{client_vm_map = OldMap ++ [NewState]}};
-
-handle_info(timeout, #state{lsock = LSock} = State) ->
- {ok, _Sock} = gen_tcp:accept(LSock),
- {noreply, State}.
-
-
-%%-----------------------------------------------------
-%% TCP Calls
-%%-----------------------------------------------------
-send(Socket, RefID, String) ->
- gen_tcp:send(Socket, io_lib:fwrite("~p ~p~n", [RefID,String])).
-
-send(Socket, RefID, String1, String2) ->
- gen_tcp:send(Socket, io_lib:fwrite("~p ~p ~p~n", [RefID, String1, String2])).
-
-
-%%-----------------------------------------------------
-%% gen_server callbacks
-%%-----------------------------------------------------
-terminate(_Reason, _State) ->
- ok.
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%-----------------------------------------------------
-%% Internal functions
-%%-----------------------------------------------------
-do_JSCall(Socket, Data, State) ->
- JSVM = js_runner:boot(),
- js_runner:define(JSVM, "function userCommand(cmd, par) {return cmd+' '+ par}"),
- Parsed = ggs_protocol:parse(Data),
- NewState = case Parsed of
- {cmd, Command, Parameter} ->
- % Set the new state to []
- Ret = js_runner:call(JSVM, "userCommand",
- [list_to_binary(Command),
- list_to_binary(Parameter)]),
- send(Socket, "RefID", "JS says: ", Ret),
- [];
- % Set the new state to the reference generated, and JSVM associated
- {hello} ->
- Client = getRef(),
- send(Socket, Client, "__ok_hello"),
- {Client, JSVM};
- {echo, RefID, _, MSG} ->
- send(Socket, RefID, "Your VM is ", getJSVM(RefID, State)),
- [];
- {crash, Zero} ->
- 10/Zero;
- {vms} ->
- send(Socket, "RefID", State);
- % Set the new state to []
- Other ->
- send(Socket, "RefID", "__error"),
- []
- end,
- % Return the new state
- NewState.
-
-%%-----------------------------------------------------
-%% Helpers
-%%-----------------------------------------------------
-getRef() ->
- {A1,A2,A3} = now(),
- random:seed(A1, A2, A3),
- random:uniform(1000).
-
-%%-----------------------------------------------------
-%% Helpers
-%%-----------------------------------------------------
-getJSVM(RefID, State) ->
- VMs = State#state.client_vm_map,
- {value, {_,VM}} = lists:keysearch(RefID, 1, VMs),
- VM.
diff --git a/src/ggs_player.erl b/src/ggs_player.erl
new file mode 100644
index 0000000..0211f3f
--- /dev/null
+++ b/src/ggs_player.erl
@@ -0,0 +1,83 @@
+-module(ggs_player).
+-export([start_link/1, notify/3, get_token/1, stop/2]).
+-record(pl_state,
+ {token, % Player's token
+ socket, % Player's socket
+ table}). % Player's table
+
+%% @doc This module handles communication between a player and GGS. This module is
+%%responsible for:
+%% * The storage of the player socket, player token and a table token.
+%% * Ability to fetch a player token.
+%% * Forwarding messages from players to the game
+%% * Remove a player from GGS
+
+%% @doc Spawns a process representing the player in GGS. Takes the player socket as
+%% an argument for storage and later usage. Creates a unique player token
+%% identifying the player.
+%% @spec start_link(Socket::socket()) -> {ok, Pid} | {error, Reason}
+start_link(Socket) ->
+ % The socket is in 'active' mode, and that means we are pushed any data
+ % that arrives on it, we do not need to recv() manually. Since the socket
+ % was opened in our parent process, we need to change the owner of it to
+ % us, otherwise these messages end up in our parent.
+ erlang:port_connect(Socket, self()),
+ {ok, Token} = ggs_coordinator:join_lobby(),
+ 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.
+
+%% @doc Handles incoming messages from the GGS and forwards them through the player
+%% socket to the player.
+%% @spec notify(Player::Pid(), From::Pid(),
+%% {Command::String(), Message::string()}) -> ok
+notify(Player, From, Message) ->
+ Player ! {notify, From, Message}.
+
+%% @doc Get the player token uniquely representing the player.
+%% @spec get_token() -> string()
+get_token(_Player) ->
+ ggs_logger:not_implemented().
+
+%% @doc Properly terminates the player process. The player token will be lost
+%% together with the table token. It should also close the player socket and the
+%% process should return in the end.
+%% @spec stop(Table::pid()) -> Reason::string()
+stop(_Player,_Table) ->
+ ggs_logger:not_implemented().
+
+%% Internals
+
+loop(#pl_state{token = _Token, socket = Socket, table = Table} = State) ->
+ receive
+ {tcp, Socket, Data} -> % Just echo for now..
+ 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);
+ % 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
index 35da585..3dc88f9 100644
--- a/src/ggs_protocol.erl
+++ b/src/ggs_protocol.erl
@@ -33,6 +33,8 @@ 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]) ->
@@ -47,11 +49,11 @@ handle_data(Data, Length) ->
prettify({Args, Data}) ->
case lists:keyfind(srv_cmd, 1, Args) of
{_, Value} ->
- gen_server:cast(ggs_server, {srv_cmd, Value, Args, Data});
+ {srv_cmd, Value, Args, Data};
_Other ->
case lists:keyfind(game_cmd, 1, Args) of
{_, Value} ->
- gen_server:cast(ggs_server, {game_cmd, Value, Args, Data});
+ {game_cmd, Value, Args, Data};
_ ->
ok
end
diff --git a/src/ggs_server.erl b/src/ggs_server.erl
deleted file mode 100644
index ab9b8f8..0000000
--- a/src/ggs_server.erl
+++ /dev/null
@@ -1,134 +0,0 @@
--module(ggs_server).
--behaviour(gen_server).
-
-%% API
--export([start_link/1,
- start_link/0,
- stop/0
- ]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2,
- handle_info/2, terminate/2, code_change/3]).
-
-
--define(SERVER, ?MODULE).
--define(DEFAULT_PORT, 1055).
-
--record(state, {port, lsock, client_vm_map = []}).
-
-%%%====================================================
-%%% API
-%%%====================================================
-
-%%-----------------------------------------------------
-%% @doc Starts the server
-%% @end
-%%-----------------------------------------------------
-start_link(Port) ->
- gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
-
-start_link() ->
- start_link(?DEFAULT_PORT).
-
-%%-----------------------------------------------------
-%% @doc Stops the server.
-%% @spec stop() -> ok
-%% @end
-%%-----------------------------------------------------
-stop() ->
- gen_server:cast(?SERVER, stop).
-
-%%-----------------------------------------------------
-%% gen_server callbacks
-%%-----------------------------------------------------
-
-init([Port]) ->
- case gen_server:call(ggs_backup, get_backup) of
- {backup_state, not_initialized} ->
- {ok, LSock} = gen_tcp:listen(Port, [{active, true},
- {reuseaddr, true}]),
- {ok, #state{port = Port, lsock = LSock}, 0};
- {backup_state, State} ->
- {ok, LSock} = gen_tcp:listen(Port, [{active, true},
- {reuseaddr, true}]),
- {ok, State#state{lsock = LSock}, 0}
- end.
-
-handle_call({backup_state, OldState}, _From, State) ->
- io:format("Received old state from backup~n"),
- {noreply, OldState}.
-
-
-handle_info({tcp, Socket, RawData}, State) ->
- ggs_protocol:parse(RawData),
- {noreply, State#state{lsock = Socket}};
-
-handle_info({tcp_closed, Socket}, State) ->
- gen_tcp:close(Socket),
- {stop, "Client closed socket", State};
-
-handle_info(timeout, #state{lsock = LSock} = State) ->
- {ok, _Sock} = gen_tcp:accept(LSock),
- {noreply, State};
-
-handle_info(Other, State) ->
- erlang:display(Other).
-
-terminate(_Reason, _State) ->
- ok.
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%-----------------------------------------------------
-%% Internal functions
-%%-----------------------------------------------------
-handle_cast(stop, State) ->
- {stop, normal, State};
-
-% Handle javascript defines
-handle_cast({srv_cmd, "define", Headers, Data}, State) ->
- Token = ggs_protocol:getToken(Headers),
- GameVM = getJSVM(Token, State),
- ggs_vm_runner:define(GameVM, Data),
- send(State#state.lsock, "Token", "Okay, defined that for you!"),
- {noreply, State};
-
-% Handle javascript calls
-handle_cast({srv_cmd, "call", Headers, Data}, State) ->
- Token = ggs_protocol:getToken(Headers),
- io:format("Got call request: ~p~n", [Data]),
- GameVM = getJSVM(Token, State),
- Ret = ggs_vm_runner:user_command(GameVM, "User", Data, []),
- send(State#state.lsock, Token, "JS says:", binary_to_list(Ret)),
- {noreply, State};
-
-% Set the new state to the reference generated, and JSVM associated
-handle_cast({srv_cmd, "hello", Headers, Data}, State) ->
- GameVM = ggs_vm_runner:start_link(),
- Client = getRef(),
- send(State#state.lsock, Client, "This is your refID"),
- OldMap = State#state.client_vm_map,
- NewState = State#state{client_vm_map = OldMap ++ [{Client, GameVM}]},
- gen_server:cast(ggs_backup, {set_backup, NewState}),
- {noreply, NewState}.
-
-%%-----------------------------------------------------
-%% Helpers
-%%-----------------------------------------------------
-getRef() ->
- string:strip(os:cmd("uuidgen"), right, $\n ).
-
-getJSVM(RefID, State) ->
- VMs = State#state.client_vm_map,
- erlang:display(RefID),
- erlang:display(VMs),
- {value, {_,VM}} = lists:keysearch(RefID, 1, VMs),
- VM.
-
-send(Socket, RefID, String) ->
- gen_tcp:send(Socket, string:join([RefID,String,"\n"], " ")).
-
-send(Socket, RefID, String1, String2) ->
- gen_tcp:send(Socket, string:join([RefID,String1, String2,"\n"], " ")).
diff --git a/src/ggs_server_sup.erl b/src/ggs_server_sup.erl
deleted file mode 100644
index 23d32f7..0000000
--- a/src/ggs_server_sup.erl
+++ /dev/null
@@ -1,48 +0,0 @@
--module(ggs_server_sup).
--behaviour(supervisor).
-
-%% API
--export([start/1, start_link/1]).
-
-%% Supervisor callbacks
--export([init/1]).
--define(SERVER, ?MODULE).
-
-start(Port) ->
- [FirstArg] = Port,
- {IntPort, _} = string:to_integer(FirstArg),
- start_link(IntPort).
-
-start_link(Port) ->
- supervisor:start_link({local, ?SERVER}, ?MODULE, [Port]).
-
-init([Port]) ->
- GGSServer = {ggs_server,
- {ggs_server, start_link, [Port]},
- permanent,
- 2000,
- worker,
- [ggs_server]
- },
- Backup = {ggs_backup,
- {ggs_backup, start_link, []},
- permanent,
- 2000,
- worker,
- [ggs_backup]
- },
- MnesiaServer = {ggs_mnesia_controller_server,
- {ggs_mnesia_controller_server, start_link, []},
- permanent,
- 2000,
- worker,
- [ggs_mnesia_controller_server]
- },
- Children = [MnesiaServer, Backup, GGSServer],
-
- RestartStrategy = { one_for_one, % Restart only crashing child
- 10, % Allow ten crashes per..
- 1 % 1 second, then crash supervisor.
- },
- {ok, {RestartStrategy, Children}}.
-
diff --git a/src/ggs_sup.erl b/src/ggs_sup.erl
index ee6f8cd..05fab65 100644
--- a/src/ggs_sup.erl
+++ b/src/ggs_sup.erl
@@ -2,29 +2,31 @@
-behaviour(supervisor).
%% API
--export([start/1, start_link/1]).
+-export([start_link/1]).
%% Supervisor callbacks
-export([init/1]).
-define(SERVER, ?MODULE).
-start(Port) ->
- [FirstArg] = Port,
- {IntPort, _} = string:to_integer(FirstArg),
- start_link(IntPort).
-
start_link(Port) ->
supervisor:start_link({local, ?SERVER}, ?MODULE, [Port]).
init([Port]) ->
- Server = {ggs_server_sup,
- {ggs_server_sup, start_link, [Port]},
+ Dispatcher = {ggs_dispatcher,
+ {ggs_dispatcher, start_link, [Port]},
permanent,
2000,
worker,
- [ggs_server_sup]
+ [ggs_dispatcher]
},
- Children = [Server],
+ Coordinator = {ggs_coordinator,
+ {ggs_coordinator, start_link, []},
+ permanent,
+ 2000,
+ worker,
+ [ggs_coordinator]
+ },
+ Children = [Dispatcher, Coordinator],
RestartStrategy = { one_for_one, % Restart only crashing child
10, % Allow ten crashes per..
diff --git a/src/ggs_table.erl b/src/ggs_table.erl
new file mode 100644
index 0000000..d6d2988
--- /dev/null
+++ b/src/ggs_table.erl
@@ -0,0 +1,125 @@
+%% @doc This module represents a table with players
+
+-module(ggs_table).
+-behaviour(gen_server).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3, notify_all_players/2, notify_game/3,
+ get_player_list/1]).
+
+-record(state, { players, game_vm } ).
+
+%% API
+-export([start_link/0,
+ add_player/2,
+ remove_player/2,
+ stop/1,
+ notify/3,
+ notify_all_players/2,
+ notify_game/3]).
+
+
+%% ----------------------------------------------------------------------
+% API implementation
+
+% @doc returns a new table
+start_link() ->
+ {ok, Pid} = gen_server:start_link(?MODULE, [], []),
+ Pid.
+
+%% @private
+call(Pid, Msg) ->
+ gen_server:call(Pid, Msg, infinity).
+
+% @doc adds a player to a table
+add_player(Table, Player) ->
+ call(Table, {add_player, Player}).
+
+% @doc removes player form a table
+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).
+
+% @doc notifies the table with a message from a player
+notify(Table, Player, Message) ->
+ gen_server:cast(Table, {notify, Player, Message}).
+
+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}).
+
+%% ----------------------------------------------------------------------
+
+%% @private
+init([]) ->
+ GameVM = ggs_gamevm_e:start_link(self()), %% @TODO: Temporary erlang gamevm
+ {ok, #state {
+ game_vm = GameVM,
+ players = [] }}.
+
+%% @private
+handle_call({add_player, Player}, _From, #state { players = Players } = State) ->
+ {reply, ok, State#state { players = [Player | Players] }};
+
+handle_call({remove_player, Player}, _From, #state { players = Players } = State) ->
+ {reply, ok, State#state { players = Players -- [Player] }};
+
+handle_call(get_player_list, _From, #state { players = Players } = State) ->
+ {reply, {ok, Players}, State};
+
+handle_call(Msg, _From, State) ->
+ error_logger:error_report([unknown_msg, Msg]),
+ {reply, ok, 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)
+ end,
+ {noreply, State};
+
+handle_cast({notify_game, Message, From}, #state { game_vm = GameVM } = State) ->
+ ggs_gamevm_e:user_command(GameVM, From, Message, ""),
+ {noreply, State};
+
+handle_cast({notify_all_players, Message}, #state{players = Players} = State) ->
+ lists:foreach(
+ fun(P) -> ggs_player:notify(P, "Server", Message) end,
+ Players
+ ),
+ {noreply, State};
+
+handle_cast(stop, State) ->
+ {stop, normal, State};
+handle_cast(Msg, S) ->
+ error_logger:error_report([unknown_msg, Msg]),
+ {noreply, S}.
+
+%% @private
+handle_info(Msg, S) ->
+ error_logger:error_report([unknown_msg, Msg]),
+ {noreply, S}.
+
+%% @private
+terminate(_Reason, _State) ->
+ ok.
+
+%% @private
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
diff --git a/src/ggs_vm_runner.erl b/src/ggs_vm_runner.erl
deleted file mode 100644
index e149c51..0000000
--- a/src/ggs_vm_runner.erl
+++ /dev/null
@@ -1,42 +0,0 @@
--module(ggs_vm_runner).
--export([start_link/0, define/2, user_command/4]).
-
-%Mattias
-start_link() ->
- erlang_js:start(),
- PortPid = spawn_link( fun() ->
- process_flag(trap_exit, true),
- {ok, Port} = js_driver:new(),
- js:define(Port, <<"function userCommand(user, command, args){return 'Hello world';}">>),
- loop(Port)
- end ),
- PortPid.
-
-
-loop(Port) ->
- receive
- {define, SourceCode} ->
- ok = js:define(Port, list_to_binary(SourceCode)),
- loop(Port);
- {user_command, User, Command, Args, From, Ref} ->
- {ok, Ret} = js:call(Port, <<"userCommand">>,
- [ list_to_binary(User),
- list_to_binary(Command),
- list_to_binary(Args)
- ]),
- From ! {Ref, Ret},
- loop(Port)
- end.
-
-
-define(GameVM, SourceCode) ->
- GameVM ! {define,SourceCode}.
-
-user_command(GameVM, User, Command, Args) ->
- Ref = make_ref(),
- GameVM ! {user_command, User, Command, Args, self(), Ref},
- receive
- {Ref, RetVal} ->
- RetVal;
- Other -> Other
- end.
diff --git a/src/helpers.erl b/src/helpers.erl
new file mode 100644
index 0000000..3a42fbf
--- /dev/null
+++ b/src/helpers.erl
@@ -0,0 +1,8 @@
+-module(helpers).
+-export([not_implemented/0, get_new_token/0]).
+
+not_implemented() ->
+ exit("Not implemented").
+
+get_new_token() ->
+ string:strip(os:cmd("uuidgen"), right, $\n ).
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 5920151..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 erlv8/ebin -pa ebin -pa src -eval 'ggs_protocol_test:test_parse().'
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_coordinator_test.erl b/tests/ggs_coordinator_test.erl
new file mode 100644
index 0000000..6ec41c6
--- /dev/null
+++ b/tests/ggs_coordinator_test.erl
@@ -0,0 +1,53 @@
+-module(ggs_coordinator_test).
+-include_lib("eunit/include/eunit.hrl").
+
+coordinator_test_() ->
+ {foreach,
+ fun() ->
+ {ok, _Coord} = ggs_coordinator:start_link(),
+ timer:sleep(100)
+ end,
+ fun(_X) ->
+ ggs_coordinator:stop("End of test"),
+ timer:sleep(100)
+ end,
+ [
+ fun test_start_link/0,
+ fun test_stop/0,
+ fun test_join_bad_table/0,
+ fun test_join_lobby/0
+ ]
+ }.
+
+test_start_link() ->
+ % Check process info
+ PInfo = whereis(ggs_coordinator),
+ ?assert((PInfo /= undefined)). % Did the server start?
+
+test_stop() ->
+ ok = ggs_coordinator:stop(""), % Extra cleaning
+ timer:sleep(100),
+ % Did it stop?
+ ?assert((whereis(ggs_coordinator)) == undefined).
+
+test_join_bad_table() ->
+ Response = ggs_coordinator:join_table("Nonexistant table"),
+ ?assert(Response == {error, no_such_table}).
+
+test_join_lobby() ->
+ {Response, _} = ggs_coordinator:join_lobby(),
+ ?assert(Response /= error).
+
+%% 'Manual' tests
+
+create_table_test() ->
+ {ok, _Coord} = ggs_coordinator:start_link(),
+ timer:sleep(100),
+ % Forcibly create a table. This functionality should be disabled
+ % in the production system, but is pretty nice for testing.
+ Response = ggs_coordinator:create_table({force, 1337}),
+ ?assert(Response == {ok, 1337}).
+
+join_good_table_test() ->
+ Response = ggs_coordinator:join_table(1337),
+ ?assert(Response == {ok, 1337}).
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
new file mode 100644
index 0000000..efe7530
--- /dev/null
+++ b/tests/ggs_player_test.erl
@@ -0,0 +1,27 @@
+-module(ggs_player_test).
+-include_lib("eunit/include/eunit.hrl").
+
+%% @doc start_link should always return ok for any valid socket. A valid socket
+%% should always return {ok, Pid} and {error, Reason} otherwise.
+start_link_test() ->
+ ggs_logger:not_implemented().
+
+%% @doc Given that start_link returned {ok, Player}. Notify shall always return ok and
+%% deliver a specified message through the socket.
+notify_test() ->
+ Player = ggs_player:start_link("bad arg"),
+ Message = {"something", ""},
+ 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
+%% player token. a valid token should be unique.
+get_token_test() ->
+ ggs_logger:not_implemented().
+
+%% @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 = ggs_player:start_link(something),
+ Table = test,
+ ok = ggs_player:stop(Player, Table).
diff --git a/tests/ggs_protocol_test.erl b/tests/ggs_protocol_test.erl
deleted file mode 100644
index 2230cbf..0000000
--- a/tests/ggs_protocol_test.erl
+++ /dev/null
@@ -1,6 +0,0 @@
--module(ggs_protocol_test).
--export([test_parse/0]).
-
-test_parse() ->
- Ret = ggs_protocol:parse("<> __define JavaScript"),
- io:format("~p~n", [Ret]).
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).
+