Merge branch 'jonte_rewrite' into jonte_hackage
Conflicts: src/ggs_server.erl src/ggs_vm_runner.erl
This commit is contained in:
commit
5e6e5cb4ad
49 changed files with 1889 additions and 703 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
*.swp
|
||||
*.sw*
|
||||
*.dump
|
||||
*.beam
|
||||
Mnesia.*
|
||||
*.swo
|
||||
|
|
21
HOWTO
21
HOWTO
|
@ -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
44
Makefile
Normal 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
32
README
|
@ -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
6
build
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
for i in `find src -name "*.erl"`
|
||||
do
|
||||
erlc -o ebin $i
|
||||
done
|
|
@ -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
17
client
|
@ -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
|
|
@ -3,7 +3,8 @@
|
|||
{vsn, "0.1.0"},
|
||||
{modules, [
|
||||
ggs_app,
|
||||
ggs_sup
|
||||
ggs_sup,
|
||||
ggs_dispatcher
|
||||
]},
|
||||
{registered, [ggs_sup]},
|
||||
{applications, [kernel, stdlib]},
|
||||
|
|
20
echo_test
20
echo_test
|
@ -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
317
games/GGSCalc/calc.glade
Normal 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
101
games/GGSCalc/calc.py
Normal 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
317
games/GGSChat/calc.glade
Normal 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
94
games/GGSChat/chat.py
Normal 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()
|
92
games/GGSChat/ggschat.glade
Normal file
92
games/GGSChat/ggschat.glade
Normal 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.
|
@ -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).
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
%% gamedb.hrl
|
||||
|
||||
-record(player, {id, name}).
|
||||
|
||||
|
||||
|
|
@ -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}
|
||||
|
|
@ -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.
|
@ -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
92
src/ggs_coordinator.erl
Normal 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
91
src/ggs_db.erl
Normal 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
5
src/ggs_db.hrl
Normal file
|
@ -0,0 +1,5 @@
|
|||
%% gamedb.hrl
|
||||
|
||||
-record(data, {key, value}).
|
||||
|
||||
|
68
src/ggs_dispatcher.erl
Normal file
68
src/ggs_dispatcher.erl
Normal 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
99
src/ggs_gamevm.erl
Normal 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
60
src/ggs_gamevm_e.erl
Normal 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
8
src/ggs_logger.erl
Normal 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).
|
|
@ -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
|
||||
%%-----------------------------------------------------
|
|
@ -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
83
src/ggs_player.erl
Normal 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.
|
|
@ -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
|
||||
|
|
|
@ -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"], " ")).
|
|
@ -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}}.
|
||||
|
|
@ -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
125
src/ggs_table.erl
Normal 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}.
|
||||
|
|
@ -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
8
src/helpers.erl
Normal 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
3
start
|
@ -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
|
|
@ -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().'
|
|
@ -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
|
53
tests/ggs_coordinator_test.erl
Normal file
53
tests/ggs_coordinator_test.erl
Normal 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
47
tests/ggs_db_test.erl
Normal 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
31
tests/ggs_gamevm_test.erl
Normal 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
27
tests/ggs_player_test.erl
Normal 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).
|
|
@ -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
38
tests/ggs_table_test.erl
Normal 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).
|
||||
|
Reference in a new issue