add game interlock
Stefan Schuermans

Stefan Schuermans commited on 2019-08-13 20:05:32
Showing 7 changed files, with 78 additions and 13 deletions.

... ...
@@ -75,6 +75,16 @@
75 75
       e.g. <code>18x8-1/256</code> for 18 pixels width, 8 pixels height
76 76
       in 256 grayscales.
77 77
     </p>
78
+    <h3>Locking</h3>
79
+    <p>
80
+      It is possible to interlock different games among each other,
81
+      i.e., to make sure that only one of the those games can be started at the
82
+      same time.
83
+      If the file <code>lockName</code> exists, it defines the name of a
84
+      mutual exclusion lock that is acquired when the game is started.
85
+      If the lock with this name is already taken by another game, the game
86
+      will not start.
87
+    </p>
78 88
     <h3>Maximum Scrore</h3>
79 89
     <p>
80 90
       The file <code>maxScore</code> configures at which score the game ends.
... ...
@@ -15,6 +15,7 @@
15 15
 #include "Format.h"
16 16
 #include "FormatFile.h"
17 17
 #include "Game.h"
18
+#include "LockNameFile.h"
18 19
 #include "Mgrs.h"
19 20
 #include "Module.h"
20 21
 #include "NameFile.h"
... ...
@@ -35,6 +36,7 @@ Game::Game(const std::string &name, Mgrs &mgrs, const Directory &dirBase):
35 36
   m_fileBackgroundColor(dirBase.getFile("backgroundColor")),
36 37
   m_fileOutStream(dirBase.getFile("outstream"), mgrs.m_streamMgr),
37 38
   m_height(0), m_width(0), m_channels(0), m_imgBuf(), m_backgroundColor(),
39
+  m_fileLockName(dirBase.getFile("lockName"), mgrs.m_lockMgr),
38 40
   m_haveTimeStep(false), m_timeStepTime(), m_opConnSounds(), m_opConnsClose()
39 41
 {
40 42
 }
... ...
@@ -72,6 +74,9 @@ void Game::updateConfig()
72 74
     m_fileOutStream.update();
73 75
   }
74 76
 
77
+  // process lock name updates
78
+  m_fileLockName.updateIfModified();
79
+
75 80
   // check config update of derived game
76 81
   updateConfigGame(doReinit, doRedraw);
77 82
 
... ...
@@ -110,19 +115,40 @@ void Game::timeCall()
110 115
   planTimeCall();
111 116
 }
112 117
 
113
-/// activate game: set up image buffer, call redraw()
114
-void Game::activate()
118
+/**
119
+ * @brief check if game can be actiavted: if interlock is free
120
+ * @return whether game can be activated (activate() might still fail)
121
+ */
122
+bool Game::canActivate()
115 123
 {
124
+  return m_fileLockName.isfree();
125
+}
126
+
127
+/**
128
+ * @brief activate game:
129
+ *        take interlock, set up image buffer, call redraw()
130
+ * @return whether game could be activated
131
+ */
132
+bool Game::activate()
133
+{
134
+  if (! m_fileLockName.take()) {
135
+    return false;
136
+  }
116 137
   createImgBuf();
117 138
   reinitialize();
118 139
   redraw();
140
+  return true;
119 141
 }
120 142
 
121
-/// deactivate game: tear down image buffer, deactivate output
143
+/**
144
+ * @brief deactivate game:
145
+ *        tear down image buffer, deactivate output, release interlock
146
+ */
122 147
 void Game::deactivate()
123 148
 {
124 149
   destroyImgBuf();
125 150
   sendFrame();
151
+  m_fileLockName.release();
126 152
 }
127 153
 
128 154
 /// check if game is active
... ...
@@ -18,6 +18,7 @@
18 18
 #include "File.h"
19 19
 #include "Format.h"
20 20
 #include "FormatFile.h"
21
+#include "LockNameFile.h"
21 22
 #include "Mgrs.h"
22 23
 #include "Module.h"
23 24
 #include "NameFile.h"
... ...
@@ -93,10 +94,23 @@ protected:
93 94
   /// process next time step of game
94 95
   virtual void timeStep() = 0;
95 96
 
96
-  /// activate game: set up image buffer, call redraw()
97
-  void activate();
97
+  /**
98
+   * @brief check if game can be actiavted: if interlock is free
99
+   * @return whether game can be activated (activate() might still fail)
100
+   */
101
+  bool canActivate();
98 102
 
99
-  /// deactivate game: tear down image buffer, deactivate output
103
+  /**
104
+   * @brief activate game:
105
+   *        take interlock, set up image buffer, call redraw()
106
+   * @return whether game could be activated
107
+   */
108
+  bool activate();
109
+
110
+  /**
111
+   * @brief deactivate game:
112
+   *        tear down image buffer, deactivate output, release interlock
113
+   */
100 114
   void deactivate();
101 115
 
102 116
   /// check if game is active
... ...
@@ -235,6 +249,7 @@ protected:
235 249
   ColorData     m_backgroundColor;     ///< background color
236 250
 
237 251
 private:
252
+  LockNameFile m_fileLockName; ///< lock name file for game interlock
238 253
   bool         m_haveTimeStep; ///< if a time step is pending
239 254
   Time         m_timeStepTime; ///< time of next time step
240 255
   OpConnSounds m_opConnSounds; ///< sound to play on operator connections
... ...
@@ -118,6 +118,10 @@ void Pong::updateConfigGame(bool &doReinit, bool &doRedraw)
118 118
  */
119 119
 bool Pong::acceptNewOpConn(const std::string &name)
120 120
 {
121
+  // game cannot be activates -> reject connection
122
+  if (! canActivate()) {
123
+    return false;
124
+  }
121 125
   // left player can join if none there yet
122 126
   if (name == m_name + c_opConnSuffixLeft && ! m_pConnLeft) {
123 127
     return true;
... ...
@@ -159,9 +163,14 @@ void Pong::newOpConn(const std::string &name, OpConn *pConn)
159 163
   if (isActive()) {
160 164
     redraw(); // player color is different for phone / computer
161 165
   }
162
-  // first player joined
166
+  // first player joined -> activate
163 167
   else {
164
-    activate();
168
+    if (! activate()) {
169
+      // activation failed (interlock), close connection as soon as possible
170
+      requestOpConnClose(pConn);
171
+      m_pConnLeft = nullptr;
172
+      m_pConnRight = nullptr;
173
+    }
165 174
   }
166 175
 }
167 176
 
... ...
@@ -104,8 +104,8 @@ bool Tetris::acceptNewOpConn(const std::string &name)
104 104
 {
105 105
   (void)name;
106 106
 
107
-  // accept player if no one there yet
108
-  return ! m_pConn;
107
+  // accept player if no one there yet and game can be activated
108
+  return ! m_pConn && canActivate();
109 109
 }
110 110
 
111 111
 /**
... ...
@@ -121,14 +121,17 @@ void Tetris::newOpConn(const std::string &name, OpConn *pConn)
121 121
 
122 122
   // player arrives and starts game
123 123
   if (! m_pConn) {
124
+    if (activate()) {
124 125
       m_pConn = pConn;
125 126
       requestOpConnSound(m_pConn, m_fileStartSound);
126
-    activate();
127
+    } else {
128
+      // activation failed (interlock), close connection as soon as possible
129
+      requestOpConnClose(pConn);
127 130
     }
128
-  // close imcoming connection as soon as possible, nothing else happens
131
+  }
132
+  // close imcoming connection as soon as possible
129 133
   else {
130 134
     requestOpConnClose(pConn);
131
-    return;
132 135
   }
133 136
 }
134 137
 
135 138