class Command
# Factory method
#
- def Command.factory(str, player)
+ def Command.factory(str, player, time=Time.now)
cmd = nil
case str
when ""
when /^%%MONITOROFF\s+(\S+)/
game_id = $1
cmd = MonitorOffCommand.new(str, player, $league.games[game_id])
+ when /^%%MONITOR2ON\s+(\S+)/
+ game_id = $1
+ cmd = Monitor2OnCommand.new(str, player, $league.games[game_id])
+ when /^%%MONITOR2OFF\s+(\S+)/
+ game_id = $1
+ cmd = Monitor2OffCommand.new(str, player, $league.games[game_id])
when /^%%HELP/
cmd = HelpCommand.new(str, player)
when /^%%RATING/
cmd = GetBuoyCountCommand.new(str, player, game_name)
when /^\s*$/
cmd = SpaceCommand.new(str, player)
+ when /^%%%[^%]/
+ # TODO: just ignore commands specific to 81Dojo.
+ # Need to discuss with 81Dojo people.
+ cmd = VoidCommand.new(str, player)
else
cmd = ErrorCommand.new(str, player)
end
+ cmd.time = time
return cmd
end
def initialize(str, player)
@str = str
@player = player
+ @time = Time.now # this should be replaced later with a real time
+ end
+ attr_accessor :time
+ end
+
+ # Dummy command which does nothing.
+ #
+ class VoidCommand < Command
+ def initialize(str, player)
+ super
+ end
+
+ def call
+ return :continue
end
end
if (@player.status == "game")
array_str = @str.split(",")
move = array_str.shift
+ if @player.game.last_move &&
+ @player.game.last_move.split(",").first == move
+ log_warning("Received two sequencial identical moves [#{move}] from #{@player.name}. The last one was ignored.")
+ return :continue
+ end
additional = array_str.shift
comment = nil
if /^'(.*)/ =~ additional
comment = array_str.unshift("'*#{$1.toeuc}")
end
- s = @player.game.handle_one_move(move, @player)
+ s = @player.game.handle_one_move(move, @player, @time)
@player.game.log_game(Kconv.toeuc(comment.first)) if (comment && comment.first && !s)
return :return if (s && @player.protocol == LoginCSA::PROTOCOL)
end
def in_game_status
rc = :continue
- s = @player.game.handle_one_move(@str, @player)
+ s = @player.game.handle_one_move(@str, @player, @time)
rc = :return if (s && @player.protocol == LoginCSA::PROTOCOL)
return rc
end
end
+ class MonitorHandler
+ def initialize(player)
+ @player = player
+ @type = nil
+ @header = nil
+ end
+ attr_reader :player, :type, :header
+
+ def ==(rhs)
+ return rhs != nil &&
+ rhs.is_a?(MonitorHandler) &&
+ @player == rhs.player &&
+ @type == rhs.type
+ end
+
+ def write_safe(game_id, str)
+ str.chomp.split("\n").each do |line|
+ @player.write_safe("##[%s][%s] %s\n" % [@header, game_id, line.chomp])
+ end
+ @player.write_safe("##[%s][%s] %s\n" % [@header, game_id, "+OK"])
+ end
+ end
+
+ class MonitorHandler1 < MonitorHandler
+ def initialize(player)
+ super
+ @type = 1
+ @header = "MONITOR"
+ end
+
+ def write_one_move(game_id, game)
+ write_safe(game_id, game.show.chomp)
+ end
+ end
+
+ class MonitorHandler2 < MonitorHandler
+ def initialize(player)
+ super
+ @type = 2
+ @header = "MONITOR2"
+ end
+
+ def write_one_move(game_id, game)
+ write_safe(game_id, game.last_move.gsub(",", "\n"))
+ end
+ end
+
# Command of MONITORON
#
class MonitorOnCommand < BaseCommandForGame
def call
if (@game)
- @game.monitoron(@player)
- @player.write_safe(@game.show.gsub(/^/, "##[MONITOR][#{@game_id}] "))
- @player.write_safe("##[MONITOR][#{@game_id}] +OK\n")
+ monitor_handler = MonitorHandler1.new(@player)
+ @game.monitoron(monitor_handler)
+ monitor_handler.write_safe(@game_id, @game.show)
end
return :continue
end
def call
if (@game)
- @game.monitoroff(@player)
+ @game.monitoroff(MonitorHandler1.new(@player))
+ end
+ return :continue
+ end
+ end
+
+ # Command of MONITOR2ON
+ #
+ class Monitor2OnCommand < BaseCommandForGame
+ def initialize(str, player, game)
+ super
+ end
+
+ def call
+ if (@game)
+ monitor_handler = MonitorHandler2.new(@player)
+ @game.monitoron(monitor_handler)
+ lines = IO::readlines(@game.logfile).join("")
+ monitor_handler.write_safe(@game_id, lines)
+ end
+ return :continue
+ end
+ end
+
+ class Monitor2OffCommand < MonitorOffCommand
+ def initialize(str, player, game)
+ super
+ end
+
+ def call
+ if (@game)
+ @game.monitoroff(MonitorHandler2.new(@player))
end
return :continue
end
end
@player.sente = nil
else
- if (@my_sente_str == "*")
+ if (@my_sente_str == "*") && !Login.handicapped_game_name?(@game_name)
rival = $league.get_player("game_waiting", @game_name, nil, @player) # no preference
elsif (@my_sente_str == "+")
rival = $league.get_player("game_waiting", @game_name, false, @player) # rival must be gote
elsif (@my_sente_str == "-")
rival = $league.get_player("game_waiting", @game_name, true, @player) # rival must be sente
else
- ## never reached
@player.write_safe(sprintf("##[ERROR] bad game option\n"))
return :continue
end
if buoy.is_new_game?(@game_name)
# The buoy game is not ready yet.
# When the game is set, it will be started.
+ @player.status = "game_waiting"
else
buoy_game = buoy.get_game(@game_name)
- if buoy_game.instance_of NilBuoyGame
+ if buoy_game.instance_of? NilBuoyGame
# error. never reach
end
+
+ moves_array = Board::split_moves(buoy_game.moves)
board = Board.new
begin
- board.set_from_moves(buoy_game.moves)
+ board.set_from_moves(moves_array)
rescue => err
# it will never happen since moves have already been checked
log_error "Failed to set up a buoy game: #{moves}"
return :continue
end
+ buoy.decrement_count(buoy_game)
Game::new(@player.game_name, @player, rival, board)
end
else
- board = Board.new
+ klass = Login.handicapped_game_name?(@game_name) || Board
+ board = klass.new
board.initial
Game::new(@player.game_name, @player, rival, board)
end
class ErrorCommand < Command
def initialize(str, player)
super
+ @msg = nil
end
+ attr_reader :msg
def call
- msg = "##[ERROR] unknown command %s\n" % [@str]
- @player.write_safe(msg)
- log_error(msg)
+ cmd = @str.chomp
+ # Aim to hide a possible password
+ cmd.gsub!(/LOGIN\s*(\w+)\s+.*/i, 'LOGIN \1...')
+ @msg = "##[ERROR] unknown command %s\n" % [cmd]
+ @player.write_safe(@msg)
+ log_error(@msg)
return :continue
end
end
#
#
class SetBuoyCommand < Command
- class WrongMoves < ArgumentError; end
def initialize(str, player, game_name, moves, count)
super(str, player)
end
# check moves
- moves_array = split_moves @moves
-
+ moves_array = Board::split_moves(@moves)
board = Board.new
begin
board.set_from_moves(moves_array)
buoy_game = BuoyGame.new(@game_name, @moves, @player.name, @count)
buoy.add_game(buoy_game)
+ @player.write_safe(sprintf("##[SETBUOY] +OK\n"))
+ log_info("A buoy game was created: %s by %s" % [@game_name, @player.name])
# if two players, who are not @player, are waiting for a new game, start it
p1 = $league.get_player("game_waiting", @game_name, true, @player)
p2 = $league.get_player("game_waiting", @game_name, false, @player)
return :continue unless p2
+ buoy.decrement_count(buoy_game)
game = Game::new(@game_name, p1, p2, board)
return :continue
rescue WrongMoves => e
log_error "Received wrong moves: %s from %s. [%s]" % [@moves, @player.name, e.message]
return :continue
end
-
- private
-
- # Split a moves line into an array of a move string.
- # If it fails to parse the moves, it raises WrongMoves.
- # @param moves a moves line. Ex. "+776FU-3334Fu"
- # @return an array of a move string. Ex. ["+7776FU", "-3334FU"]
- #
- def split_moves(moves)
- ret = []
-
- rs = moves.gsub %r{[\+\-]\d{4}\w{2}} do |s|
- ret << s
- ""
- end
- raise WrongMoves, rs unless rs.empty?
-
- return ret
- end
end
#
end
buoy.delete_game(buoy_game)
+ @player.write_safe(sprintf("##[DELETEBUOY] +OK\n"))
log_info("A buoy game was deleted: %s" % [@game_name])
return :continue
end
buoy = Buoy.new
buoy_game = buoy.get_game(@game_name)
if buoy_game.instance_of?(NilBuoyGame)
- @player.write_safe("##[GETBUOYCOUNT] 0\n")
+ @player.write_safe("##[GETBUOYCOUNT] -1\n")
else
@player.write_safe("##[GETBUOYCOUNT] %s\n" % [buoy_game.count])
end