X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=shogi_server%2Fcommand.rb;h=b09b95ab78d03e43228cefe83d9ae76d77ad74f7;hb=ec8a83d45186449f11d0dfb63e0b7fad9c902378;hp=571a18d1379933595d013fc2447f3dada409d059;hpb=9118bc521d13e741387febc815aaf6e8961c8ba4;p=shogi-server%2Fshogi-server.git diff --git a/shogi_server/command.rb b/shogi_server/command.rb index 571a18d..b09b95a 100644 --- a/shogi_server/command.rb +++ b/shogi_server/command.rb @@ -1,7 +1,7 @@ ## $Id$ ## Copyright (C) 2004 NABEYA Kenichi (aka nanami@2ch) -## Copyright (C) 2007-2008 Daigo Moriwaki (daigo at debian dot org) +## Copyright (C) 2007-2012 Daigo Moriwaki (daigo at debian dot org) ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by @@ -69,6 +69,9 @@ module ShogiServer my_sente_str = $3 cmd = GameChallengeCommand.new(str, player, command_name, game_name, my_sente_str) + when /^%%(GAME|CHALLENGE)\s+(\S+)/ + msg = "A turn identifier is required" + cmd = ErrorCommand.new(str, player, msg) when /^%%CHAT\s+(.+)/ message = $1 cmd = ChatCommand.new(str, player, message, $league.players) @@ -94,8 +97,25 @@ module ShogiServer when /^%%GETBUOYCOUNT\s+(\S+)/ game_name = $1 cmd = GetBuoyCountCommand.new(str, player, game_name) + when /^%%FORK\s+(\S+)\s+(\S+)(.*)/ + source_game = $1 + new_buoy_game = $2 + nth_move = nil + if $3 && /^\s+(\d+)/ =~ $3 + nth_move = $3.to_i + end + cmd = ForkCommand.new(str, player, source_game, new_buoy_game, nth_move) + when /^%%FORK\s+(\S+)$/ + source_game = $1 + new_buoy_game = nil + nth_move = nil + cmd = ForkCommand.new(str, player, source_game, new_buoy_game, nth_move) 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 @@ -112,6 +132,18 @@ module ShogiServer attr_accessor :time end + # Dummy command which does nothing. + # + class VoidCommand < Command + def initialize(str, player) + super + end + + def call + return :continue + 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 @@ -374,10 +406,17 @@ module ShogiServer end end - class Monitor2OffCommand < MonitorOffCommand # same + class Monitor2OffCommand < MonitorOffCommand def initialize(str, player, game) super end + + def call + if (@game) + @game.monitoroff(MonitorHandler2.new(@player)) + end + return :continue + end end # Command of HELP @@ -453,11 +492,21 @@ module ShogiServer @command_name = command_name @game_name = game_name @my_sente_str = my_sente_str + player.set_sente_from_str(@my_sente_str) end def call if (! Login::good_game_name?(@game_name)) - @player.write_safe(sprintf("##[ERROR] bad game name\n")) + @player.write_safe(sprintf("##[ERROR] bad game name: %s.\n", @game_name)) + if (/^(.+)-\d+-\d+$/ =~ @game_name) + if Login::good_identifier?($1) + # do nothing + else + @player.write_safe(sprintf("##[ERROR] invalid identifiers are found or too many characters are used.\n")) + end + else + @player.write_safe(sprintf("##[ERROR] game name should consist of three parts like game-1500-60.\n")) + end return :continue elsif ((@player.status == "connected") || (@player.status == "game_waiting")) ## continue @@ -467,14 +516,6 @@ module ShogiServer end rival = nil - if (Buoy.game_name?(@game_name)) - if (@my_sente_str != "*") - @player.write_safe(sprintf("##[ERROR] You are not allowed to specify TEBAN %s for the game %s\n", @my_sente_str, @game_name)) - return :continue - end - @player.sente = nil - end # really end - if (League::Floodgate.game_name?(@game_name)) if (@my_sente_str != "*") @player.write_safe(sprintf("##[ERROR] You are not allowed to specify TEBAN %s for the game %s\n", @my_sente_str, @game_name)) @@ -482,42 +523,18 @@ module ShogiServer end @player.sente = nil else - 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 - @player.write_safe(sprintf("##[ERROR] bad game option\n")) - return :continue + rival = $league.find_rival(@player, @game_name) + if rival.instance_of?(Symbol) + # An error happened. rival is not a player instance, but an error + # symobl that must be returned to the main routine immediately. + return rival end end if (rival) @player.game_name = @game_name - - if ((@my_sente_str == "*") && (rival.sente == nil)) - if (rand(2) == 0) - @player.sente = true - rival.sente = false - else - @player.sente = false - rival.sente = true - end - elsif (rival.sente == true) # rival has higher priority - @player.sente = false - elsif (rival.sente == false) - @player.sente = true - elsif (@my_sente_str == "+") - @player.sente = true - rival.sente = false - elsif (@my_sente_str == "-") - @player.sente = false - rival.sente = true - else - ## never reached - end + Game::decide_turns(@player, @my_sente_str, rival) + if (Buoy.game_name?(@game_name)) buoy = Buoy.new # TODO config if buoy.is_new_game?(@game_name) @@ -552,13 +569,6 @@ module ShogiServer if (@command_name == "GAME") @player.status = "game_waiting" @player.game_name = @game_name - if (@my_sente_str == "+") - @player.sente = true - elsif (@my_sente_str == "-") - @player.sente = false - else - @player.sente = nil - end else # challenge @player.write_safe(sprintf("##[ERROR] can't find rival for %s\n", @game_name)) @player.status = "connected" @@ -681,14 +691,19 @@ module ShogiServer # Command for an error # class ErrorCommand < Command - def initialize(str, player) - super + def initialize(str, player, msg=nil) + super(str, player) + @msg = msg || "unknown command" 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] %s: %s\n" % [@msg, cmd] + @player.write_safe(@msg) + log_error(@msg) return :continue end end @@ -736,15 +751,32 @@ module ShogiServer @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) - return :continue unless p1 - p2 = $league.get_player("game_waiting", @game_name, false, @player) - return :continue unless p2 - + # if two players are waiting for this buoy game, start it + candidates = $league.find_all_players do |player| + player.status == "game_waiting" && + player.game_name == @game_name && + player.name != @player.name + end + if candidates.empty? + log_info("No players found for a buoy game. Wait for players: %s" % [@game_name]) + return :continue + end + p1 = candidates.first + p2 = $league.find_rival(p1, @game_name) + if p2.nil? + log_info("No opponent found for a buoy game. Wait for the opponent: %s by %s" % [@game_name, p1.name]) + return :continue + elsif p2.instance_of?(Symbol) + # An error happened. rival is not a player instance, but an error + # symobl that must be returned to the main routine immediately. + return p2 + end + # found two players: p1 and p2 + log_info("Starting a buoy game: %s with %s and %s" % [@game_name, p1.name, p2.name]) buoy.decrement_count(buoy_game) game = Game::new(@game_name, p1, p2, board) return :continue + rescue WrongMoves => e @player.write_safe(sprintf("##[ERROR] wrong moves: %s\n", @moves)) log_error "Received wrong moves: %s from %s. [%s]" % [@moves, @player.name, e.message] @@ -799,6 +831,72 @@ module ShogiServer @player.write_safe("##[GETBUOYCOUNT] %s\n" % [buoy_game.count]) end @player.write_safe("##[GETBUOYCOUNT] +OK\n") + return :continue + end + end + + # %%FORK [] + # Fork a new game from the posistion where the n-th (starting from 1) move + # of a source game is played. The new game should be a valid buoy game + # name. The default value of n is the position where the previous position + # of the last one. + # + class ForkCommand < Command + def initialize(str, player, source_game, new_buoy_game, nth_move) + super(str, player) + @source_game = source_game + @new_buoy_game = new_buoy_game + @nth_move = nth_move # may be nil + end + attr_reader :new_buoy_game + + def decide_new_buoy_game_name + name = nil + total_time = nil + byo_time = nil + + if @source_game.split("+").size >= 2 && + /^([^-]+)-(\d+)-(\d+)/ =~ @source_game.split("+")[1] + name = $1 + total_time = $2 + byo_time = $3 + end + if name == nil || total_time == nil || byo_time == nil + @player.write_safe(sprintf("##[ERROR] wrong source game name to make a new buoy game name: %s\n", @source_game)) + log_error "Received a wrong source game name to make a new buoy game name: %s from %s." % [@source_game, @player.name] + return :continue + end + @new_buoy_game = "buoy_%s_%d-%s-%s" % [name, @nth_move, total_time, byo_time] + @player.write_safe(sprintf("##[FORK]: new buoy game name: %s\n", @new_buoy_game)) + @player.write_safe("##[FORK] +OK\n") + end + + def call + game = $league.games[@source_game] + unless game + @player.write_safe(sprintf("##[ERROR] wrong source game name: %s\n", @source_game)) + log_error "Received a wrong source game name: %s from %s." % [@source_game, @player.name] + return :continue + end + + moves = game.read_moves # [["+7776FU","T2"],["-3334FU","T5"]] + @nth_move = moves.size - 1 unless @nth_move + if @nth_move > moves.size or @nth_move < 1 + @player.write_safe(sprintf("##[ERROR] number of moves to fork is out of range: %s.\n", moves.size)) + log_error "Number of moves to fork is out of range: %s [%s]" % [@nth_move, @player.name] + return :continue + end + new_moves_str = "" + moves[0...@nth_move].each do |m| + new_moves_str << m.join(",") + end + + unless @new_buoy_game + decide_new_buoy_game_name + end + + buoy_cmd = SetBuoyCommand.new(@str, @player, @new_buoy_game, new_moves_str, 1) + return buoy_cmd.call end end