Compare commits

...
Sign in to create a new pull request.

7 commits
TCP ... master

Author SHA1 Message Date
Loic Guegan
8e31de0793 Add admin interface 2019-05-12 14:01:05 +02:00
Loic Guegan
8cba94e081 Update api 2019-05-12 11:16:16 +02:00
Loic Guegan
4ce40ee01d Remove useless files 2019-05-12 11:03:51 +02:00
Loic Guegan
b00ab9b38a Update gitignore 2019-05-12 11:03:31 +02:00
Loic Guegan
df0fcb5952 Add score 2019-05-12 11:00:08 +02:00
Loic Guegan
bbd9151d7f Switch to udp 2019-05-12 10:53:33 +02:00
Loic Guegan
9de3c7586d Update client 2019-05-12 10:53:22 +02:00
11 changed files with 77 additions and 61 deletions

6
.gitignore vendored
View file

@ -1,2 +1,8 @@
# Ignore tex files # Ignore tex files
*.tex *.tex
# Ignore projects files
**.vscode
# Ignore cache files
**__pycache__

View file

@ -1,3 +0,0 @@
{
"python.pythonPath": "/usr/bin/python"
}

View file

@ -1,3 +1,4 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import socket, json, time import socket, json, time
import pygame import pygame
@ -6,25 +7,19 @@ import pygame
RESP_BUFFER_LENGTH = 1024 RESP_BUFFER_LENGTH = 1024
ip_adress="192.168.1.14" ip_adress="192.168.1.14"
port=8090 port=8080
LARGEUR_BLOCK = 10 LARGEUR_BLOCK = 10
NB_BLOCKS = 30 NB_BLOCKS = 30
LARGEUR_ECRAN = LARGEUR_BLOCK * NB_BLOCKS LARGEUR_ECRAN = LARGEUR_BLOCK * NB_BLOCKS
def connect(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip_adress,port))
return s
def sendData(data): def sendData(data):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.sendto(data,(ip_adress,port))
s.connect((ip_adress,port)) received = s.recvfrom(1024)
s.sendall(data.encode('utf-8')) return received[0]
s.send('#EOF\n'.encode('utf-8'))
received = s.recv(RESP_BUFFER_LENGTH)
s.close()
return received
def newGame(): def newGame():
received = sendData('{"type": "new-game"}') received = sendData('{"type": "new-game"}')
@ -39,6 +34,7 @@ def update(gameId = 1, direction = None):
received = sendData(json.dumps(data)) received = sendData(json.dumps(data))
return json.loads(received) return json.loads(received)
# ---------- END SOCKETS ---------- # ---------- END SOCKETS ----------
LARGEUR_BLOCK = 20 LARGEUR_BLOCK = 20
@ -47,7 +43,7 @@ LARGEUR_ECRAN = LARGEUR_BLOCK * NB_BLOCKS
pygame.init() pygame.init()
ecran = pygame.display.set_mode((LARGEUR_ECRAN,LARGEUR_ECRAN)) ecran = pygame.display.set_mode((LARGEUR_ECRAN,LARGEUR_ECRAN))
myfont = pygame.font.SysFont('Comic Sans MS', 20)
snakeSprite = pygame.image.load("snake-sprite.png").convert_alpha() snakeSprite = pygame.image.load("snake-sprite.png").convert_alpha()
snakeSprite = pygame.transform.scale(snakeSprite, (LARGEUR_BLOCK, LARGEUR_BLOCK)) snakeSprite = pygame.transform.scale(snakeSprite, (LARGEUR_BLOCK, LARGEUR_BLOCK))
@ -81,28 +77,29 @@ def handleControls(event, gameId):
def main(): def main():
gameInit = newGame() gameInit = newGame()
gameId = gameInit['ID'] gameId = gameInit['id']
up = gameInit up = gameInit
initSize=len(up['snake'])
continuer = True continuer = True
while continuer: while continuer:
afficher(ecran, up['SNAKE'], up['FOOD']) afficher(ecran, up['snake'], up['food'])
ecran.blit(myfont.render('Score {}'.format(len(up['snake'])-initSize), False, (0, 0, 0)),(0,0))
for event in pygame.event.get(): for event in pygame.event.get():
if event.type == pygame.QUIT: if event.type == pygame.QUIT:
continuer = False continuer = False
elif event.type == pygame.KEYDOWN: elif event.type == pygame.KEYDOWN:
updateTmp = handleControls(event, gameId) updateTmp = handleControls(event, gameId)
print(updateTmp) print(updateTmp)
if updateTmp != None and 'TYPE' in updateTmp: if updateTmp != None and 'type' in updateTmp:
if updateTmp['TYPE'] != 'error': if updateTmp['type'] != 'error':
up = updateTmp up = updateTmp
time.sleep(0.2) time.sleep(0.3)
up = update(gameId)
pygame.display.flip() pygame.display.flip()
up = update(gameId)
pygame.quit() pygame.quit()
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View file

@ -4,14 +4,10 @@
#+LATEX_HEADER: \usepackage{fullpage} #+LATEX_HEADER: \usepackage{fullpage}
* General Description * General Description
- All transmissions will be based on TCP since: - All transmissions will be based on UDP since latency is important
- Packet length are not fixed (large variance depending on the snake size and food) - All UDP datagrams between *client and server* will contain _plain json data_
- Packet ordering is important (inverted request can compromise gameplay) - All data should be sent in *one* datagram
- All TCP streams from *client to server* will: - All utf-8 characters in UDP datagram are in lower case
- Contain _plain json data_
- Be terminated by an "#EOF" line (in order for the server to detect the end of the client request)
- All TCP stream from *server to client* will contains _plain json data_ (connection will be closed by the server
so, there is no need of "#EOF").
* Communications * Communications
** Initialisation ** Initialisation
1. Client sent: 1. Client sent:
@ -19,7 +15,6 @@
{ {
"type": "new-game" "type": "new-game"
} }
#EOF
#+END_SRC #+END_SRC
2. Server can reply: 2. Server can reply:
#+BEGIN_SRC json #+BEGIN_SRC json
@ -40,7 +35,6 @@
"game-id": 1, "game-id": 1,
"direction": "left", "direction": "left",
} }
#EOF
#+END_SRC #+END_SRC
2. Then, server can reply: 2. Then, server can reply:
#+BEGIN_SRC json #+BEGIN_SRC json
@ -60,7 +54,6 @@
"game-id": 1, "game-id": 1,
"direction": null "direction": null
} }
#EOF
#+END_SRC #+END_SRC
2. Server can reply: 2. Server can reply:
#+BEGIN_SRC json #+BEGIN_SRC json

Binary file not shown.

View file

@ -33,6 +33,7 @@
((equal dir "down") (setf (getf p-request :direction) :down)) ((equal dir "down") (setf (getf p-request :direction) :down))
((equal dir "left") (setf (getf p-request :direction) :left)) ((equal dir "left") (setf (getf p-request :direction) :left))
((equal dir "right") (setf (getf p-request :direction) :right)))))) ((equal dir "right") (setf (getf p-request :direction) :right))))))
((equal type "admin"))
((not (equal type "new-game")) ((not (equal type "new-game"))
(error 'bad-request :msg "Unknown request type"))) (error 'bad-request :msg "Unknown request type")))
p-request))) p-request)))
@ -43,8 +44,9 @@
(let* ((game-id (create-game gm))) (let* ((game-id (create-game gm)))
(let ((game-dump (dump gm game-id))) (let ((game-dump (dump gm game-id)))
(setf (getf game-dump :game-over) :null) ; Define nil as null (for json) (setf (getf game-dump :game-over) :null) ; Define nil as null (for json)
(to-json (string-downcase
(append (list :type "state") game-dump)))))) (to-json
(append (list :type "state") game-dump)))))))
(defmethod handle-update ((api api) data) (defmethod handle-update ((api api) data)
(with-slots (gm) api (with-slots (gm) api
@ -54,9 +56,20 @@
(if dir (if dir
(refresh game :dir dir) (refresh game :dir dir)
(refresh game)) (refresh game))
(to-json (string-downcase
(append (list :type "state") (dump gm game-id)))))) (to-json
(append (list :type "state") (dump gm game-id)))))))
;;; TODO: debug this function
(defmethod handle-admin ((api api) data)
(with-slots (gm) api
(let* ((game-id (getf data :game-id))
(cmd (getf data :cmd))
(arg (getf data :arg))
(game (get-game gm game-id)))
(cond
((equal cmd "move") (admin game :move arg)))))
"Command executed!")
(defmethod handle-request ((api api) request) (defmethod handle-request ((api api) request)
;; Catch request error and send it to the client ;; Catch request error and send it to the client
@ -66,6 +79,7 @@
(cond (cond
((equal type "new-game") (handle-new-game api data)) ((equal type "new-game") (handle-new-game api data))
((equal type "update") (handle-update api data)) ((equal type "update") (handle-update api data))
((equal type "admin") (handle-admin api data))
(t (format t "Unknow type")))) (t (format t "Unknow type"))))
(error (condition) ; Send reason to the client (error (condition) ; Send reason to the client
(let ((reason (make-array 0 (let ((reason (make-array 0

View file

@ -21,6 +21,12 @@
:initform nil :initform nil
:accessor game-over))) :accessor game-over)))
;;; Admin function
(defmethod admin ((g game) &key (move nil move-supplied-p))
(with-slots (snake) g
(when move-supplied-p (setf (nth 0 snake) move))))
;;; Class constructor to initialize the snake ;;; Class constructor to initialize the snake
(defmethod initialize-instance :after ((g game) &key) (defmethod initialize-instance :after ((g game) &key)
(with-slots (snake initial-size initial-position) g (with-slots (snake initial-size initial-position) g

View file

@ -4,7 +4,8 @@
(:export (:export
#:game #:game
#:dump #:dump
#:refresh)) #:refresh
#:admin))
(defpackage :remote-snake-server-api (defpackage :remote-snake-server-api
(:nicknames :rsapi) (:nicknames :rsapi)
@ -16,6 +17,7 @@
(defpackage :remote-snake-server (defpackage :remote-snake-server
(:nicknames :rss) (:nicknames :rss)
(:use :common-lisp :usocket :remote-snake-server-api :cl-strings) (:use :common-lisp :usocket :remote-snake-server-api :cl-strings :jonathan)
(:export (:export
#:start)) #:start
#:send-cmd))

View file

@ -3,7 +3,7 @@
:description "Remote Snake Server." :description "Remote Snake Server."
:version "0.0.1" :version "0.0.1"
:author "Loic Guegan" :author "Loic Guegan"
:depends-on ( "usocket" "jonathan" "cl-strings" ) :depends-on ( "usocket-server" "jonathan" "cl-strings" )
:components ((:file "packages") :components ((:file "packages")
(:module "game" (:module "game"
:depends-on ("packages") :depends-on ("packages")

View file

@ -6,24 +6,25 @@
(defparameter *server-buffer* (make-array 10000 (defparameter *server-buffer* (make-array 10000
:element-type '(unsigned-byte 8) :element-type '(unsigned-byte 8)
:initial-element 0)) :initial-element 0))
;;; TODO: debug this function
(defun send-cmd (host port game-id command arg)
(let ((socket (usocket:socket-connect host port :protocol :datagram))
(request (list :type "admin" :cmd command :game-id game-id :arg arg))
(buffer (make-array 500 :element-type '(unsigned-byte 8) :initial-element 0)))
(usocket:socket-send socket (string-downcase (to-json request)) 300)
(format t (babel:octets-to-string (usocket:socket-receive socket buffer 300)))
(force-output t)
(usocket:socket-close socket)))
(defun handle-client (client-socket)
(unwind-protect ; To be sure to close the client socket
(let* ((request (read-line (usocket:socket-stream client-socket)))) (defun handle-client (buffer) ; echo
(loop while (eq (search "#EOF" request) nil) do (declare (type (simple-array (unsigned-byte 8) *) buffer)) ; Seems to be to tell lisp which type is buffer
(setf request (concatenate 'string request (read-line (usocket:socket-stream client-socket) nil nil)))) (let ((request (babel:octets-to-string buffer)))
(setf request (cl-strings:replace-all request "#EOF" "")) (format t "Receive client request: ~a" request)
(format (usocket:socket-stream client-socket) (handle-request *server-api* request)) (babel:string-to-octets
(force-output (usocket:socket-stream client-socket))) (handle-request *server-api* request))))
(usocket:socket-close client-socket)))
;;; The server :D ;;; The server :D
(defun start (interface port) (defun start (host port)
(format t "Server started!~%") (usocket:socket-server host port #'handle-client nil :in-new-thread nil :protocol :datagram))
(let ((socket (usocket:socket-listen interface port)))
(unwind-protect ; To be sure to close server socket
(loop
(handle-client (usocket:socket-accept socket :element-type 'character)))
(usocket:socket-close socket))))