OSDN Git Service

[shogi-server] Bump up the revision to 20201206 master
authorDaigo Moriwaki <beatles@sgtpepper.net>
Sun, 6 Dec 2020 09:37:55 +0000 (18:37 +0900)
committerDaigo Moriwaki <beatles@sgtpepper.net>
Sun, 6 Dec 2020 09:37:55 +0000 (18:37 +0900)
changelog
shogi-server
shogi_server.rb
shogi_server/board.rb
shogi_server/command.rb
shogi_server/player.rb
test/TC_board.rb
test/TC_command.rb
test/mock_log_message.rb

index b3df157..0dc1e9b 100644 (file)
--- a/changelog
+++ b/changelog
@@ -1,9 +1,41 @@
+2020-12-06  Daigo Moriwaki <daigo at debian dot org>
+
+       * [shogi-server] Improve timed-up detection (continued).
+         The server now checks timed up when it receives a-single-space keep
+         alive messages as well.
+         Thanks to mizar for reports and patches.
+         (Closes #40821)
+       * [shogi-server] Support listening on IPv6 addresses
+         Thanks to mizar for a patch.
+         (Closes #40822)
+       * [shogi-server] Make invalid comments illegal
+         Some client sent moves with comments in an invalid format like
+         "+7776FU '* 30 -3334FU +2726FU". Such messages are now deemed
+         illegal.
+         Thanks to mizar for a report.
+       * [shogi-server] Bump up the revision to 20201206
+
+2020-10-04  Daigo Moriwaki <daigo at debian dot org>
+
+       * [shogi-server] Improve timed-up detection.
+         Previously, the server checked if a game got timed up when a player
+         in turn sent no message for a certain amount of time mainly defined
+         by Default_Timeout. If the player sent keep alive frequently, the
+         timed-up detection could be quite delayed.
+         This issue has been addressed. The server now checks timed up with
+         keep alive received as well. Players are notified with TIME_UP not
+         long before games gets timed up.
+
 2018-08-25  Daigo Moriwaki <daigo at debian dot org>
 
        * [shogi-server] Support a graceful shutdown.
          A file named "STOP" in the base directory prevents the server from
          starting new games including Floodgate matches.
          (Closes #38544)
+       * [shogi-server] Create a directory for a PID file.
+         To put a PID file such as /var/run/shogi-server/shogi-server.pid, if
+         directories do not exist, they will be created recursively.
+         (Closes #38546)
 
 2018-04-07  Daigo Moriwaki <daigo at debian dot org>
 
index 21bc9d1..e78d421 100755 (executable)
@@ -34,6 +34,8 @@ require 'shogi_server'
 require 'shogi_server/config'
 require 'shogi_server/util'
 require 'shogi_server/league/floodgate_thread.rb'
+require 'pathname'
+require 'set'
 require 'tempfile'
 
 #################################################
@@ -271,6 +273,8 @@ def check_command_line
 
   if $options["pid-file"] 
     $options["pid-file"] = File.expand_path($options["pid-file"], $topdir)
+    path = Pathname.new($options["pid-file"])
+    path.dirname().mkpath()
     unless ShogiServer::is_writable_file? $options["pid-file"]
       usage
       $stderr.puts "Can not create the pid file: %s" % [$options["pid-file"]]
@@ -432,8 +436,11 @@ def main
 
   $league.dir = $topdir
 
+  # Set of connected players
+  $players = Set.new
+
   config = {}
-  config[:BindAddress] = "0.0.0.0"
+  config[:BindAddress] = nil # both IPv4 and IPv6
   config[:Port]       = port
   config[:ServerType] = WEBrick::Daemon if $options["daemon"]
   config[:Logger]     = $logger
@@ -459,8 +466,9 @@ def main
 
   srand
   server = WEBrick::GenericServer.new(config)
-  ["INT", "TERM"].each do |signal| 
+  ["INT", "TERM"].each do |signal|
     trap(signal) do
+      $players.each {|p| p.kill}
       server.shutdown
       setup_floodgate.kill
     end
@@ -492,6 +500,14 @@ def main
       log_message(sprintf("user %s login", player.name))
       login.process
       player.setup_logger($options["player-log-dir"]) if $options["player-log-dir"]
+
+      $mutex.lock
+      begin
+       $players.add(player)
+      ensure
+        $mutex.unlock
+      end
+
       player.run(login.csa_1st_str) # loop
       $mutex.lock
       begin
@@ -501,6 +517,7 @@ def main
         player.finish
         $league.delete(player)
         log_message(sprintf("user %s logout", player.name))
+       $players.delete(player)
       ensure
         $mutex.unlock
       end
index 536f9c3..5e8a87c 100644 (file)
@@ -53,7 +53,7 @@ Default_Max_Moves = 256
 Default_Least_Time_Per_Move = 0
 One_Time = 10
 Login_Time = 300                # time for LOGIN
-Revision = "20180825"
+Revision = "20201206"
 
 RELOAD_FILES = ["shogi_server/league/floodgate.rb",
                 "shogi_server/league/persistent.rb",
index 4c9c607..b494111 100644 (file)
@@ -642,7 +642,7 @@ EOF
   #   - :max_moves
   #
   def handle_one_move(str, sente=nil)
-    if (str =~ /^([\+\-])(\d)(\d)(\d)(\d)([A-Z]{2})/)
+    if (str =~ /^([\+\-])(\d)(\d)(\d)(\d)([A-Z]{2})$/)
       sg = $1
       x0 = $2.to_i
       y0 = $3.to_i
index ad8684d..41bd626 100644 (file)
@@ -27,13 +27,11 @@ module ShogiServer
     #
     def Command.factory(str, player, time=Time.now)
       cmd = nil
-      case str 
-      when "" 
-        cmd = KeepAliveCommand.new(str, player)
+      case str
+      when "", " ", /^%[^%]/, :timeout
+        cmd = SpecialCommand.new(str, player)
       when /^[\+\-][^%]/
         cmd = MoveCommand.new(str, player)
-      when /^%[^%]/, :timeout
-        cmd = SpecialCommand.new(str, player)
       when :exception
         cmd = ExceptionCommand.new(str, player)
       when /^REJECT/
@@ -145,22 +143,6 @@ module ShogiServer
     end
   end
 
-  # Application-level protocol for Keep-Alive.
-  # If the server receives an LF, it sends back an LF.  Note that the 30 sec
-  # rule (client may not send LF again within 30 sec) is not implemented
-  # yet.
-  #
-  class KeepAliveCommand < Command
-    def initialize(str, player)
-      super
-    end
-
-    def call
-      @player.write_safe("\n")
-      return :continue
-    end
-  end
-
   # Command of moving a piece.
   #
   class MoveCommand < Command
@@ -190,7 +172,17 @@ module ShogiServer
     end
   end
 
-  # Command like "%TORYO" or :timeout
+  # Command like "%TORYO", :timeout, or keep alive
+  #
+  # Keep Alive is an application-level protocol here. There are two representations:
+  # 1) LF (empty string)
+  #    The server sends back an LF (empty string).
+  #    Note that the 30 sec rule (client may not send LF again within 30 sec)
+  #    is not implemented yet.
+  #    This is compliant with CSA's protocol in certain situations.
+  # 2) Space + LF (a single space)
+  #    The sever replies nothing.
+  #    This is an enhancement to CSA's protocol.
   #
   class SpecialCommand < Command
     def initialize(str, player)
@@ -199,6 +191,14 @@ module ShogiServer
 
     def call
       rc = :continue
+
+      if @str == "" || @str == " " # keep alive
+        log_debug("received keep alive from #{@player.name}")
+        @player.write_safe("\n") if @str == ""
+        # Fall back to :timeout to check the game gets timed up
+        @str = :timeout
+      end
+
       if (@player.status == "game")
         rc = in_game_status()
       elsif ["agree_waiting", "start_waiting"].include?(@player.status) 
index c7dc8a4..7a1c303 100644 (file)
@@ -301,7 +301,8 @@ class Player < BasicPlayer
           @socket_buffer << str
           str = @socket_buffer.shift
         end
-        log_debug("%s (%s)" % [str, @socket_buffer.map {|a| String === a ? a.strip : a }.join(",")])
+        log_debug("dump socket buffer: '%s' (%s)" % [String === str ? str.strip : str,
+                                                    @socket_buffer.map {|a| String === a ? a.strip : a }.join(",")])
 
         if (csa_1st_str)
           str = csa_1st_str
index 06e4efe..5b47b0a 100644 (file)
@@ -998,3 +998,23 @@ class TestSplitMoves < Test::Unit::TestCase
     end
   end
 end
+
+
+class Test_illegal < Test::Unit::TestCase
+  def test_invaild_comment
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
+EOM
+
+    assert_equal(:illegal, b.handle_one_move("+7776FU '* 30 -3334FU +2726FU"))
+  end
+end
index 440ec34..cd49f24 100644 (file)
@@ -90,7 +90,12 @@ class TestFactoryMethod < Test::Unit::TestCase
 
   def test_keep_alive_command
     cmd = ShogiServer::Command.factory("", @p)
-    assert_instance_of(ShogiServer::KeepAliveCommand, cmd)
+    assert_instance_of(ShogiServer::SpecialCommand, cmd)
+  end
+
+  def test_keep_alive_command_space
+    cmd = ShogiServer::Command.factory(" ", @p)
+    assert_instance_of(ShogiServer::SpecialCommand, cmd)
   end
 
   def test_move_command
@@ -214,7 +219,7 @@ class TestFactoryMethod < Test::Unit::TestCase
   end
 
   def test_space_command
-    cmd = ShogiServer::Command.factory(" ", @p)
+    cmd = ShogiServer::Command.factory("  ", @p)
     assert_instance_of(ShogiServer::SpaceCommand, cmd)
   end
 
@@ -315,20 +320,6 @@ end
 
 #
 #
-class TestKeepAliveCommand < Test::Unit::TestCase 
-  def setup
-    @p = MockPlayer.new
-  end
-
-  def test_call
-    cmd = ShogiServer::KeepAliveCommand.new("", @p)
-    rc = cmd.call
-    assert_equal(:continue, rc)
-  end
-end
-
-#
-#
 class TestMoveCommand < Test::Unit::TestCase
   def setup
     @p = MockPlayer.new
@@ -350,6 +341,13 @@ class TestMoveCommand < Test::Unit::TestCase
     assert_equal("'*comment", @game.log.first)
   end
 
+  def test_comment_illegal
+    cmd = ShogiServer::MoveCommand.new("+7776FU 'comment", @p)
+    rc = cmd.call
+    assert_equal(:continue, rc)
+    assert_nil(@game.log.first)
+  end
+
   def test_x1_return
     @game.finish_flag = true
     @p.protocol = ShogiServer::LoginCSA::PROTOCOL
@@ -417,6 +415,18 @@ class TestSpecialComand < Test::Unit::TestCase
     rc = cmd.call
     assert_equal(:continue, rc)
   end
+
+  def test_keep_alive
+    cmd = ShogiServer::SpecialCommand.new("", @p)
+    rc = cmd.call
+    assert_equal(:continue, rc)
+  end
+
+  def test_keep_alive_space
+    cmd = ShogiServer::SpecialCommand.new(" ", @p)
+    rc = cmd.call
+    assert_equal(:continue, rc)
+  end
 end
 
 #
@@ -822,7 +832,7 @@ class TestSpaceCommand < Test::Unit::TestCase
   end
 
   def test_call
-    cmd = ShogiServer::SpaceCommand.new("", @p)
+    cmd = ShogiServer::SpaceCommand.new("  ", @p)
     rc = cmd.call
 
     assert_equal(:continue, rc)
index 5ffeb84..193c8d7 100644 (file)
@@ -30,3 +30,7 @@ def log_info(msg)
   $logger.info(msg)
 end
 
+def log_debug(msg)
+  $logger.info(msg)
+end
+