Merge branch 'jonte_rewrite' into jonte_hackage

Conflicts:
	src/ggs_server.erl
	src/ggs_vm_runner.erl
This commit is contained in:
Jonatan Pålsson 2011-02-24 11:23:06 +01:00
commit 5e6e5cb4ad
49 changed files with 1889 additions and 703 deletions

3
.gitignore vendored
View file

@ -1,4 +1,5 @@
*.swp
*.sw*
*.dump
*.beam
Mnesia.*
*.swo

21
HOWTO
View file

@ -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

44
Makefile Normal file
View file

@ -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

32
README
View file

@ -1,3 +1,35 @@
GGS is a Generic Game Server
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)

6
build
View file

@ -1,6 +0,0 @@
#!/usr/bin/env bash
for i in `find src -name "*.erl"`
do
erlc -o ebin $i
done

View file

@ -1,8 +0,0 @@
#!/usr/bin/env bash
./build
for i in `find tests -name "*.erl"`
do
erlc -o ebin_test $i
done

17
client
View file

@ -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

View file

@ -3,7 +3,8 @@
{vsn, "0.1.0"},
{modules, [
ggs_app,
ggs_sup
ggs_sup,
ggs_dispatcher
]},
{registered, [ggs_sup]},
{applications, [kernel, stdlib]},

View file

@ -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

@ -1 +1 @@
Subproject commit 5350ed21606606dbee5ecb07e974f2abb9106270
Subproject commit 2f2785fafb0da6db75810eb6fa97d09c58257588

317
games/GGSCalc/calc.glade Normal file
View file

@ -0,0 +1,317 @@
<?xml version="1.0" encoding="UTF-8"?>
<glade-interface>
<!-- interface-requires gtk+ 2.16 -->
<!-- interface-naming-policy project-wide -->
<widget class="GtkWindow" id="window1">
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkEntry" id="txtCalc">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="n_rows">5</property>
<property name="n_columns">4</property>
<child>
<widget class="GtkButton" id="button1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="btnDiv">
<property name="label" translatable="yes">/</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnDiv_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnMul">
<property name="label" translatable="yes">*</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnMul_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnMin">
<property name="label" translatable="yes">-</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnMin_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn7">
<property name="label" translatable="yes">7</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn7_clicked"/>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn8">
<property name="label" translatable="yes">8</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn8_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn9">
<property name="label" translatable="yes">9</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn9_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnPlus">
<property name="label" translatable="yes">+</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnPlus_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn4">
<property name="label" translatable="yes">4</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn4_clicked"/>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn5">
<property name="label" translatable="yes">5</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn5_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn6">
<property name="label" translatable="yes">6</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn6_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnDel">
<property name="label" translatable="yes">←</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnDel_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn1">
<property name="label" translatable="yes">1</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn1_clicked"/>
</widget>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn2">
<property name="label" translatable="yes">2</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn2_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn3">
<property name="label" translatable="yes">3</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn3_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnEq">
<property name="label" translatable="yes">=</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnEq_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn0">
<property name="label" translatable="yes">0</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn0_clicked"/>
</widget>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button18">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button19">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnConnect">
<property name="label" translatable="yes">☎</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnConnect_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkStatusbar" id="statusbar">
<property name="visible">True</property>
<property name="spacing">2</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

101
games/GGSCalc/calc.py Normal file
View file

@ -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()

317
games/GGSChat/calc.glade Normal file
View file

@ -0,0 +1,317 @@
<?xml version="1.0" encoding="UTF-8"?>
<glade-interface>
<!-- interface-requires gtk+ 2.16 -->
<!-- interface-naming-policy project-wide -->
<widget class="GtkWindow" id="window1">
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkEntry" id="txtCalc">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkTable" id="table1">
<property name="visible">True</property>
<property name="n_rows">5</property>
<property name="n_columns">4</property>
<child>
<widget class="GtkButton" id="button1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</widget>
</child>
<child>
<widget class="GtkButton" id="btnDiv">
<property name="label" translatable="yes">/</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnDiv_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnMul">
<property name="label" translatable="yes">*</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnMul_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnMin">
<property name="label" translatable="yes">-</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnMin_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn7">
<property name="label" translatable="yes">7</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn7_clicked"/>
</widget>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn8">
<property name="label" translatable="yes">8</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn8_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn9">
<property name="label" translatable="yes">9</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn9_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnPlus">
<property name="label" translatable="yes">+</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnPlus_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn4">
<property name="label" translatable="yes">4</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn4_clicked"/>
</widget>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn5">
<property name="label" translatable="yes">5</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn5_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn6">
<property name="label" translatable="yes">6</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn6_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnDel">
<property name="label" translatable="yes">←</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnDel_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn1">
<property name="label" translatable="yes">1</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn1_clicked"/>
</widget>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn2">
<property name="label" translatable="yes">2</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn2_clicked"/>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn3">
<property name="label" translatable="yes">3</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn3_clicked"/>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnEq">
<property name="label" translatable="yes">=</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnEq_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btn0">
<property name="label" translatable="yes">0</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btn0_clicked"/>
</widget>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button18">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="button19">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
</widget>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="btnConnect">
<property name="label" translatable="yes">☎</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_btnConnect_clicked"/>
</widget>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkStatusbar" id="statusbar">
<property name="visible">True</property>
<property name="spacing">2</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

94
games/GGSChat/chat.py Normal file
View file

@ -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()

View file

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<glade-interface>
<!-- interface-requires gtk+ 2.16 -->
<!-- interface-naming-policy project-wide -->
<widget class="GtkWindow" id="window1">
<property name="default_width">500</property>
<property name="default_height">500</property>
<signal name="destroy_event" handler="on_window1_destroy_event"/>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<widget class="GtkTextView" id="chatBox">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<signal name="focus" handler="on_chatBox_focus"/>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<child>
<widget class="GtkEntry" id="nickBox">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
<property name="width_chars">10</property>
<property name="text" translatable="yes">Anonymous</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">•</property>
<signal name="activate" handler="on_entry_activate"/>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="sendButton">
<property name="label" translatable="yes">Chat!</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_sendButton_clicked"/>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkStatusbar" id="statusbar">
<property name="visible">True</property>
<property name="spacing">2</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

Binary file not shown.

View file

@ -1,50 +0,0 @@
%%%%----------------------------------------------------
%%% @author Mattias Pettersson <mattiaspgames@gmail.com>
%%% @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).

View file

@ -1,6 +0,0 @@
%% gamedb.hrl
-record(player, {id, name}).

View file

@ -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}

View file

@ -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()

Binary file not shown.

Binary file not shown.

View file

@ -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.

92
src/ggs_coordinator.erl Normal file
View file

@ -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}.

91
src/ggs_db.erl Normal file
View file

@ -0,0 +1,91 @@
%%%%----------------------------------------------------
%%% @author Mattias Pettersson <mattiaspgames@gmail.com>
%%% @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.

5
src/ggs_db.hrl Normal file
View file

@ -0,0 +1,5 @@
%% gamedb.hrl
-record(data, {key, value}).

68
src/ggs_dispatcher.erl Normal file
View file

@ -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 = #<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}.

99
src/ggs_gamevm.erl Normal file
View file

@ -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).

60
src/ggs_gamevm_e.erl Normal file
View file

@ -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.

8
src/ggs_logger.erl Normal file
View file

@ -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).

View file

@ -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
%%-----------------------------------------------------

View file

@ -1,170 +0,0 @@
%%%----------------------------------------------------
%%% @author Jonatan Pålsson <Jonatan.p@gmail.com>
%%% @copyright 2010 Jonatan Pålsson
%%% @doc RPC over TCP server
%%% @end
%%%----------------------------------------------------
%%% @author Mattias Pettersson <mattiaspgames@gmail.com>
%%% @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.

83
src/ggs_player.erl Normal file
View file

@ -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.

View file

@ -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

View file

@ -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"], " ")).

View file

@ -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}}.

View file

@ -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..

125
src/ggs_table.erl Normal file
View file

@ -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}.

View file

@ -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.

8
src/helpers.erl Normal file
View file

@ -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 ).

3
start
View file

@ -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

View file

@ -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().'

View file

@ -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

View file

@ -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}).

47
tests/ggs_db_test.erl Normal file
View file

@ -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().

31
tests/ggs_gamevm_test.erl Normal file
View file

@ -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;"}))).

27
tests/ggs_player_test.erl Normal file
View file

@ -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).

View file

@ -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]).

38
tests/ggs_table_test.erl Normal file
View file

@ -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).