~lthms/lycan.lisp

2bfff68d2e665bd9908a9a0780cce3a80292caf0 — Thomas Letan 1 year, 6 months ago 34d3155
feature: Spawn new instances of a single scene on demand

This patch introduces several key concept of the lycan daemon. The
game is organized on a set of interconnected scenes. It is possible to
set a limit for the number of players allowed on a given scene
simultaneously. In case a new player wants to register to a scene
which is already full, the daemon will spawn a new instance for this
scene. Note that the current implementation still lacks of a key
feature: killing empty instances.

Signed-off-by: Thomas Letan <contact@thomasletan.fr>
4 files changed, 105 insertions(+), 32 deletions(-)

M daemon.lisp
A daemon/main.lisp
A daemon/scene.lisp
M lycan.asd
M daemon.lisp => daemon.lisp +1 -30
@@ 16,34 16,5 @@

(defpackage :lycan/daemon
  (:nicknames :ld)
  (:use :cl)
  (:use :cl :alexandria)
  (:export main))

(cl:in-package :lycan/daemon)

(defclass daemon ()
  ((players :initform nil
            :accessor players)))

(defun make-daemon ()
  (make-instance 'daemon))

(defmethod register-new-player ((dm daemon) socket)
  (push socket (players dm)))

(defmethod init ((g daemon))
  (as:tcp-server
   "127.0.0.1"
   4000
   nil
   :connect-cb (lambda (socket) (register-new-player g socket))))

(defmethod frame-step ((dm daemon))
  (dolist (socket (players dm))
    (as:write-socket-data socket "hi")))

(defun main ()
  (let ((dm (make-daemon)))
    (as:with-event-loop (:catch-app-errors t)
      (init dm)
      (as:interval (lambda () (frame-step dm)) :time 1))))

A daemon/main.lisp => daemon/main.lisp +48 -0
@@ 0,0 1,48 @@
;; lycan.lisp: yet another game server for the lycan project
;; Copyright (C) 2018 Thomas Letan <contact@thomasletan.fr>
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU Affero General Public License as published
;; by the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

(cl:in-package :lycan/daemon)

(defclass daemon ()
  ((players :initform nil
            :accessor players)))

(defvar *map* (make-instance 'scene :limit 3))
(defvar *pool* (make-instance 'pool :scene *map*))

(defun make-daemon ()
  (make-instance 'daemon))

(defmethod register-new-player ((dm daemon) socket)
  (register-player *pool* socket)
  (push socket (players dm)))

(defmethod init ((g daemon))
  (as:tcp-server
   "127.0.0.1"
   4000
   nil
   :connect-cb (lambda (socket) (register-new-player g socket))))

(defmethod frame-step ((dm daemon))
  (dolist (socket (players dm))
    (as:write-socket-data socket "hi")))

(defun main ()
  (let ((dm (make-daemon)))
    (as:with-event-loop (:catch-app-errors t)
      (init dm)
      (as:interval (lambda () (frame-step dm)) :time 1))))

A daemon/scene.lisp => daemon/scene.lisp +52 -0
@@ 0,0 1,52 @@
;; lycan.lisp: yet another game server for the lycan project
;; Copyright (C) 2018 Thomas Letan <contact@thomasletan.fr>
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU Affero General Public License as published
;; by the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU Affero General Public License for more details.
;;
;; You should have received a copy of the GNU Affero General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

(cl:in-package :lycan/daemon)

(defclass scene ()
  ((limit :initarg :limit
          :initform nil
          :reader limit)))

(defgeneric register-player (target player))

(defclass pool ()
  ((scene :initarg :scene
          :initform (error "A pool needs a scene")
          :reader scene)
   (instances :initform nil
              :accessor instances)))

(defmethod register-player ((target pool) player)
  (if (not (dolist (i (instances target))
             (when (register-player i player)
               (return t))))
      (let ((i (make-instance 'instance :scene (scene target))))
        (push i (instances target))
        (register-player i player))))

(defclass instance ()
  ((scene :initarg :scene
          :initform (error "An instance needs a scene")
          :reader scene)
   (players :initform nil
            :accessor players)))

(defmethod register-player ((target instance) player)
  (if-let ((limit (limit (scene target))))
    (when (< (length (players target)) limit)
      (push player (players target)))
    (push player (players target))))

M lycan.asd => lycan.asd +4 -2
@@ 20,5 20,7 @@
  :license  "AGPLv3"
  :version "3.0.0"
  :serial t
  :depends-on (cl-async)
  :components ((:file "daemon")))
  :depends-on (cl-async alexandria)
  :components ((:file "daemon")
               (:file "daemon/scene")
               (:file "daemon/main")))