BCDice/TRPGツールからの呼び出し方/どどんとふ

どどんとふからのBCDiceの呼び出し方。このページでは、Rubyで書かれたどどんとふのサーバのダイス関連の処理を扱う。v1.49.04.01のソースコードを参考にしている。

各ファイルの役割

どどんとふのサーバでは、ダイスボットとの通信の処理は以下のファイルに記述されている。

src_ruby/dodontof/dice_adapter.rb:BCDiceとのアダプタ

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/src_ruby/dodontof/dice_adapter.rb

ダイスロールと関係していることがすぐに分かるファイル名だが、実は大部分は独自の表からの情報の読み出しやファイル管理の処理となっている。

L7-L10:DiceAdapter#initialize

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/src_ruby/dodontof/dice_adapter.rb#L7-L10

アダプタを初期化する。

  • @dir:部屋固有のデータ保存ディレクトリが設定される。詳細は「#L164-L185:@dice_adapter の初期化」で解説する。
  • @diceBotTablePrefix:必ず 'diceBotTable_' が設定される。
def initialize(dir, prefix)
  @logger = DodontoF::Logger.instance
  @dir = dir
  @diceBotTablePrefix = prefix
end

L13-L34:DiceAdapter#rollDice

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/src_ruby/dodontof/dice_adapter.rb#L13-L34

ダイスロールを行う。メッセージ、ゲームシステム、出目を返すかが指定できる。結果のメッセージ、シークレットダイスかどうか、出目が返る。結果のメッセージの '>''→' に置換され、末尾の改行コードは削除される。

def rollDice(params)
  require 'cgiDiceBot.rb'

  message = params['message']
  gameType = params['gameType']
  isNeedResult = params['isNeedResult']

  @logger.debug(message, 'rollDice message')
  @logger.debug(gameType, 'rollDice gameType')

  bot = CgiDiceBot.new

  result, randResults = bot.roll(message, gameType, @dir, @diceBotTablePrefix, isNeedResult)

  result.gsub!(/>/, '→')
  result.sub!(/\r?\n?\Z/m, '')

  @logger.debug(result, 'rollDice result')
  @logger.debug(randResults, 'rollDice randResults')

  return result, bot.isSecret, randResults
end

L36-L45:DiceAdapter#getGameCommandInfos

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/src_ruby/dodontof/dice_adapter.rb#L36-L45

独自の表について、ゲームタイプ(ゲームの識別子)および含まれるコマンドの情報を返す。CgiDiceBot#getGameCommandInfos を経由して TableFileData#getGameCommandInfos を呼び出している。

def getGameCommandInfos
  require 'cgiDiceBot.rb'
  bot = CgiDiceBot.new
  @logger.debug(@dir, 'dir')

  commandInfos = bot.getGameCommandInfos(@dir, @diceBotTablePrefix)
  @logger.debug(commandInfos, "getGameCommandInfos End commandInfos")

  return commandInfos
end

L47-L63:DiceAdapter#getBotTableInfosFromDir

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/src_ruby/dodontof/dice_adapter.rb#L47-L63

独自の表についての情報を取得する。getGameCommandInfos と似ているが、こちらの方が返される情報が詳しい?

def getBotTableInfosFromDir
  @logger.debug(@dir, 'getBotTableInfosFromDir dir')

  require 'TableFileData'

  isLoadCommonTable = false
  tableFileData = TableFileData.new( isLoadCommonTable )
  tableFileData.setDir(@dir, @diceBotTablePrefix)
  tableInfos = tableFileData.getAllTableInfo

  @logger.debug(tableInfos, "getBotTableInfosFromDir tableInfos")
  tableInfos.sort!{|a, b| a["command"].to_i <=> b["command"].to_i}

  @logger.debug(tableInfos, 'getBotTableInfosFromDir result tableInfos')

  return tableInfos
end

L65-L84:DiceAdapter#addBotTableMain

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/src_ruby/dodontof/dice_adapter.rb#L65-L84

独自の表のファイルを保存する。

def addBotTableMain(params)
  @logger.debug("addBotTableMain Begin")

  DodontoF::Utils.makeDir(@dir)

  require 'TableFileData'

  resultText = 'OK'
  begin
    creator = TableFileCreator.new(@dir, @diceBotTablePrefix, params)
    creator.execute
  rescue Exception => e
    @logger.exception(e)
    resultText = getLanguageKey( e.to_s )
  end

  @logger.debug(resultText, "addBotTableMain End resultText")

  return resultText
end

L86-L103:DiceAdapter#changeBotTableMain

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/src_ruby/dodontof/dice_adapter.rb#L86-L103

独自の表のファイルを更新する。

def changeBotTableMain(params)
  @logger.debug("changeBotTableMain Begin")

  require 'TableFileData'

  resultText = 'OK'
  begin
    creator = TableFileEditer.new(@dir, @diceBotTablePrefix, params)
    creator.execute
  rescue Exception => e
    @logger.exception(e)
    resultText = getLanguageKey( e.to_s )
  end

  @logger.debug(resultText, "changeBotTableMain End resultText")

  return resultText
end

L105-L135:DiceAdapter#removeBotTableMain

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/src_ruby/dodontof/dice_adapter.rb#L86-L103

独自の表のファイルを削除する。

def removeBotTableMain(params)
  @logger.debug("removeBotTableMain Begin")

  command = params["command"]

  require 'TableFileData'

  isLoadCommonTable = false
  tableFileData = TableFileData.new( isLoadCommonTable )
  tableFileData.setDir(@dir, @diceBotTablePrefix)
  tableInfos = tableFileData.getAllTableInfo

  tableInfo = tableInfos.find{|i| i["command"] == command}
  @logger.debug(tableInfo, "tableInfo")
  return if( tableInfo.nil? )

  fileName = tableInfo["fileName"]
  @logger.debug(fileName, "fileName")
  return if( fileName.nil? )

  @logger.debug("isFile exist?")
  return unless( File.exist?(fileName) )

  begin
    File.delete(fileName)
  rescue Exception => e
    @logger.exception(e)
  end

  @logger.debug("removeBotTableMain End")
end

src_ruby/diceBotInfos.rb:ダイスボットの情報を取得する処理

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/src_ruby/diceBotInfos.rb

L69-L102:DiceBotInfos.get

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/src_ruby/diceBotInfos.rb#L69-L102

# ダイスボットの情報の一覧を取得する
# @param [Array<String>] orderedGameNames 順序付けられたゲーム名の配列
# @param [Boolean] showAllDiceBots すべてのダイスボットを表示するか
# @return [Array<Hash>]
def self.get(orderedGameNames, showAllDiceBots)
  diceBots = DiceBotLoader.collectDiceBots

  # ゲーム名 => ダイスボットの対応を作る
  diceBotFor = Hash[
    diceBots.map { |diceBot| [diceBot.gameName, diceBot] }
  ]
  toDiceBot = lambda { |gameName| diceBotFor[gameName] }

  orderedEnabledDiceBots = orderedGameNames.
    map(&toDiceBot).
    # ゲーム名が誤記されていた場合nilになるので除く
    compact

  orderedDiceBots =
    if showAllDiceBots
      disabledGameNames = diceBotFor.keys - orderedGameNames
      orderedDisabledDiceBots = disabledGameNames.
        sort.
        map(&toDiceBot)

      # 一覧に記載されていたゲーム→記載されていなかったゲームの順
      orderedEnabledDiceBots + orderedDisabledDiceBots
    else
      orderedEnabledDiceBots
    end

  # 指定なし→各ゲーム→基本の順で返す
  [NONE_DICE_BOT_INFO] + orderedDiceBots.map(&:info) + [BASE_DICE_BOT_INFO]
end

L104-150:DiceBotInfos.withTableCommands

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/src_ruby/diceBotInfos.rb#L104-150

# テーブルのコマンドも加えたダイスボットの情報の一覧を取得する
# @param [Array<String>] orderedGameNames 順序付けられたゲーム名の配列
# @param [Boolean] showAllDiceBots すべてのダイスボットを表示するか
# @param [Array<Hash>] commandInfos テーブルのコマンドの情報の配列
# @return [Array<Hash>]
#
# このメソッドは、ダイスボットから返された情報を破壊しない。
def self.withTableCommands(orderedGameNames, showAllDiceBots, commandInfos)
  diceBotInfos = self.get(orderedGameNames, showAllDiceBots)

  # ゲームタイプ => ダイスボット情報のインデックスの対応を作る
  diceBotInfoIndexFor = Hash[
    diceBotInfos.each_with_index.map { |info, i| [info[KEY_GAME_TYPE], i] }
  ]
  allGameTypes = diceBotInfoIndexFor.keys

  # ゲームタイプ => 追加するコマンドの一覧の対応を作る
  commandsToAdd = commandInfos.reduce({}) { |acc, commandInfo|
    gameType = commandInfo[KEY_GAME_TYPE]
    command = commandInfo[KEY_COMMAND]

    # ゲームタイプ未指定ならすべてのゲームタイプに追加する
    targetGameTypes = gameType.empty? ? allGameTypes : [gameType]

    targetGameTypes.each do |targetGameType|
      acc[targetGameType] ||= []
      acc[targetGameType] << command
    end

    acc
  }

  commandsToAdd.each do |gameType, commands|
    diceBotInfoIndex = diceBotInfoIndexFor[gameType]
    next unless diceBotInfoIndex

    originalInfo = diceBotInfos[diceBotInfoIndex]

    # ダイスボットから返された情報を破壊しないようにして更新する
    diceBotInfos[diceBotInfoIndex] = originalInfo.merge({
      KEY_PREFIXS => originalInfo[KEY_PREFIXS] + commands
    })
  end

  diceBotInfos
end

DodontoFServer.rb:サーバ本体

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb

L164-L185:@dice_adapter の初期化

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L164-L185

def initialize(saveDirInfo, cgiParams)
  @cgiParams = cgiParams
  @saveDirInfo = saveDirInfo

  @logger = DodontoF::Logger.instance
  @cgi = nil

  @jsonpCallBack = nil
  @isWebIf = false
  @isRecordEmpty = false

  initSaveFiles(getRequestData('room'))
  @dice_adapter = DodontoF::DiceAdapter.new(getDiceBotExtraTableDirName, 'diceBotTable_')

  @fullBackupFileBaseName = "DodontoFFullBackup"

  @allSaveDataFileExt = '.tar.gz'
  @defaultAllSaveData = 'default.sav'
  @defaultChatPallete = 'default.cpd'

  @card = nil
end

getDiceBotExtraTableDirNameL3727-L3729)では、getRoomLocalSpaceDirNameL3262-L3265)、getRoomLocalSpaceDirNameByRoomNoL3267-L3273)を経由して、$imageUploadDir の中の部屋固有のディレクトリを返す。

L1193-L1210:DodontoFServer#getWebIfServerInfo

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L1193-L1210

Webインターフェースのサーバ情報を返すコマンド。getDiceBotInfos でダイスボットの情報を取得する。

def getWebIfServerInfo()
  jsonData = {
    "maxRoom" => ($saveDataMaxCount - 1),
    'isNeedCreatePassword' => (not $createPlayRoomPassword.empty?),
    'result' => 'OK',
  }

  if( getWebIfRequestBoolean("card", false) )
    cardInfos = getCardsInfo.collectCardTypeAndTypeName($cardOrder)
    jsonData["cardInfos"] = cardInfos
  end

  if( getWebIfRequestBoolean("dice", false) )
    jsonData['diceBotInfos'] = getDiceBotInfos()
  end

  return jsonData
end

L2090:DodontoFServer#getLoginInfo

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L2090

ダイスボット情報の取得を行う。

diceBotInfos = getDiceBotInfos()

L2266-L2280:DodontoFServer#getDiceBotInfos

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L2266-L2280

ダイスボット情報取得処理の実装。部屋に入っているかどうかで、テーブルのコマンドも含めた情報を返すかどうかを決定している。

def getDiceBotInfos
  @logger.debug("getDiceBotInfos() Begin")

  require 'diceBotInfos'

  orderedGameNames = $diceBotOrder.split("\n")

  if @saveDirInfo.getSaveDataDirIndex != -1
    DiceBotInfos.withTableCommands(orderedGameNames,
                                   $isDisplayAllDice,
                                   @dice_adapter.getGameCommandInfos)
  else
    DiceBotInfos.get(orderedGameNames, $isDisplayAllDice)
  end
end

L2542-2556:DodontoFServer#save, getDiceTableData

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L2542-2556

部屋のデータの保存時に、独自の表についての情報を取得する。

def save()
  isAddPlayRoomInfo = true
  extension = @@saveFileExtension

  addInfos = {}
  addInfos[$diceBotTableSaveKey] = getDiceTableData()

  saveSelectFiles($saveFiles.keys, extension, isAddPlayRoomInfo, addInfos)
end

def getDiceTableData()
  tableInfos = @dice_adapter.getBotTableInfosFromDir
  tableInfos.each{|i| i.delete('fileName') }
  return tableInfos
end

L2806-L2817:DodontoFServer#getBotTableInfos

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L2806-L2817

独自の表についての情報を取得する。

def getBotTableInfos()
  @logger.debug("getBotTableInfos Begin")
  result = {
    "resultText"=> "OK",
  }

  result["tableInfos"] = @dice_adapter.getBotTableInfosFromDir

  @logger.debug(result, "result")
  @logger.debug("getBotTableInfos End")
  return result
end

L2819-L2835:DodontoFServer#addBotTable

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L2819-L2835

独自の表を追加する。

def addBotTable()
  result = {}

  params = getParamsFromRequestData()
  result['resultText'] = @dice_adapter.addBotTableMain(params)

  if( result['resultText'] != "OK" )
    return result
  end

  @logger.debug("addBotTableMain called")

  result = getBotTableInfos()
  @logger.debug(result, "addBotTable result")

  return result
end

L2837-L2849:DodontoFServer#changeBotTable

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L2837-L2849

独自の表を変更する。

def changeBotTable()
  params = getParamsFromRequestData()

  result = {}
  result['resultText'] = @dice_adapter.changeBotTableMain(params)

  if( result['resultText'] != "OK" )
    return result
  end

  result = getBotTableInfos()
  return result
end

L2851-L2855:DodontoFServer#removeBotTable

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L2851-L2855

独自の表を削除する。

def removeBotTable()
  params = getParamsFromRequestData()
  @dice_adapter.removeBotTableMain(params)
  return getBotTableInfos()
end

L3458-L3459:DodontoFServer#loadSaveFileDataFilterByTargets

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L3458-L3459

セーブデータの部分読み込み処理の一部。独自の表を読み込む。

when "diceBotTable"
  loadDiceBotTable(jsonData)

L3511-L3531:DodontoFServer#loadDiceBotTable, DodontoFServer#getDiceBotTableString

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L3511-L3531

独自の表を読み込む。

def loadDiceBotTable(jsonData)

  data = jsonData[$diceBotTableSaveKey]
  return if( data.nil? )

  data.each do |info|
    info['table'] = getDiceBotTableString(info['table'])
    @dice_adapter.addBotTableMain(info)
  end

end

def getDiceBotTableString(table)

  lines = []
  table.each do |line|
    lines << line.join(":")
  end

  return lines.join("\n")
end

L3632-L3657:DodontoFServer#sendDiceBotChatMessage

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L3632-L3657

BCDiceにコマンドを送る処理。回数 コマンド という形で送信回数が指定されていた場合は、指定された回数だけコマンドをBCDiceに送る。

def sendDiceBotChatMessage
  @logger.debug('sendDiceBotChatMessage')

  params = getParamsFromRequestData()

  repeatCount = getDiceBotRepeatCount(params)

  results = []

  repeatCount.times do |i|

    paramsClone = params.clone
    paramsClone['message'] += " \##{ i + 1 }" if( repeatCount > 1 )

    result = sendDiceBotChatMessageOnece( paramsClone )
    @logger.debug(result, "sendDiceBotChatMessageOnece result")

    next if( result.nil? )

    results << result
  end

  @logger.debug(results, "sendDiceBotChatMessage results")

  return results
end

L3659-L3669:DodontoFServer#getDiceBotRepeatCount

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L3659-L3669

コマンド送信回数を1〜20回に制限する。最近のRubyでは (params['repeatCount'] || 1).clamp(1, 20) と簡潔に書ける。

def getDiceBotRepeatCount(params)
  repeatCountLimit = 20

  repeatCount = params['repeatCount']

  repeatCount ||= 1
  repeatCount = 1 if( repeatCount < 1 )
  repeatCount = repeatCountLimit if( repeatCount > repeatCountLimit )

  return repeatCount
end

L3672-L3709:DodontoFServer#sendDiceBotChatMessageOnece

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L3672-L3709

BCDiceに1回分(おそらく Oneceonce のスペルミス)のコマンドを送る処理。getRollDiceResult でダイスロールした結果を取得する。sendChatMessageByChatDataL3817-3839)で、その結果を含むメッセージを送信する。

def sendDiceBotChatMessageOnece(params)

  rolledMessage, isSecret, secretMessage = getRollDiceResult( params )

  senderName = params['name']
  unless /\t/ === senderName
    state = params['state']
    senderName += ("\t" + state)  unless( state.empty? )
  end

  chatData = {
    "senderName" => senderName,
    "message" => rolledMessage,
    "color" => params['color'],
    "uniqueId" => '0',
    "channel" => params['channel']
  }

  sendto = params['sendto']
  unless( sendto.nil? )
    chatData['sendto'] = sendto
    chatData['sendtoName'] = sendtoName
  end

  @logger.debug(chatData, 'sendDiceBotChatMessageOnece chatData')

  sendChatMessageByChatData(chatData)


  result = nil
  if( isSecret )
    params['isSecret'] = isSecret
    params['message'] = secretMessage
    result = params
  end

  return result
end

L3672-L3725:DodontoFServer#getRollDiceResult

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L3672-L3709

BCDiceでダイスを振る。シークレットダイスかどうかも考慮してメッセージを加工し、返す。

def getRollDiceResult( params )
  params['originalMessage'] = params['message']
  rollResult, isSecret, randResults = @dice_adapter.rollDice(params)

  secretMessage = ""
  if( isSecret )
    secretMessage = params['message'] + rollResult
  else
    params['message'] += rollResult
  end

  rolledMessage = getRolledMessage(params, isSecret, randResults)

  return rolledMessage, isSecret, secretMessage
end

L3732-L3762:DodontoFServer#getRolledMessage

https://github.com/torgtaitai/DodontoF/blob/d3e4cc374885986efc761cfabbb19e4b35e815e0/DodontoFServer.rb#L3732-L3709

ダイスロールの結果を入力として、どどんとふクライアントでの表示に必要な情報を付加する。シークレットダイスの場合に結果を隠すことと、「!」の数でサイコロを強調できるようにすることが目的のようだ。

def getRolledMessage(params, isSecret, randResults)
  @logger.debug("getRolledMessage Begin")

  @logger.debug(isSecret, "isSecret")
  @logger.debug(randResults, "randResults")

  if( isSecret )
    params['message'] = getLanguageKey('secretDice')
    randResults = randResults.collect{|value, max| [0, 0] }
  end

  message = params['message']

  if( randResults.nil? )
    @logger.debug("randResults is nil")
    return message
  end


  data = {
    "chatMessage" => message,
    "randResults" => randResults,
    "uniqueId" => params['uniqueId'],
    "power" => getDiceBotPower(params),
  }

  text = "###CutInCommand:rollVisualDice###" + getJsonString(data)
  @logger.debug(text, "getRolledMessage End text")

  return text
end