回線速度を計測して、Steamの帯域制限を自動で設定するようにした

## きっかけ

Steamの回線が丈夫なのか、アップデートなどでダウンロードが始まると閲覧しているYoutubeなどのロードが追いつかなくなることがありました。

Steamにはダウンロード帯域制限機能がありますので、はじめは設定画面からちまちま設定していたのですが…

時間帯や日によって毎度毎度手作業で帯域を設定するのが面倒くさいので、何か自動でできる方法がないかと調べました

## インターネット速度の計測方法

ブラウザ経由でFast.comやSpeedtest.netを使って回線速度を調べて、Steamで設定するという方法を今までとっていましたが

https://www.speedtest.net/ja/apps/cli

Speedtest CLIがコマンドラインで実行できてとてもよさそうです。

ただ実行するだけではなく、csvやjsonに吐き出すことが可能

```speedtest.exe -f json > speedtest.txt```

みたいな感じで呼び出すとJSONのテキストを吐き出してくれます。

## Steamの帯域制限を設定する方法

この辺を参考にしました。

https://developer.valvesoftware.com/wiki/Command_Line_Options

`set_download_throttle`、`get_download_throttle`とそれっぽいものが。

設定を反映させたいときは+を付けて呼び出すようです。

適当に容量の大きいゲームのDLを始めてみて、動作を確認します。DLが始まったあと

`<Steamのインストールフォルダ>Steam.exe +set_download_throttle 160000`

と試しに実行をしてみたところ、起動中のSteamが再ロードされホーム画面へ移動しました。

ダウンロード画面を見ると設定した帯域制限速度(20MB/S)でDLが続いていることを確認できます。(試したときは20MB/sとかでやってしまったのですが、極端な遅い値などに設定すると確認しやすくて良いと思います)

これでSteamを手動で再起動する必要がないことがわかりました。助かる。

## 自動で設定するように

コマンドプロンプトでバッチファイルを書こうと思ったんですが良く知らないため、Node.jsで行います。あとでElectronとかに乗っけて操作しやすいようにしようかな…

Node.jsの`child_process`を利用すると、外部プログラムを実行して標準出力を受け取ることができます。それを利用して、上記のアプリケーションを組み合わせます。

以下はソースです。
上記Speedtest CLIのexeを同じフォルダにおいて、index.jsを書きます。

```js:index.js
const { exec } = require("child_process")

const SPEED_TEST_COMMAND = 'speedtest.exe -f json';
const STEAM_EXE = "\"C:\\Program Files (x86)\\Steam\\Steam.exe\""
const STEAM_THROTTLE_PARAM = "+set_download_throttle"
const THROTTLE_RATIO = 0.8;

function execSpeedTest() {
    exec(SPEED_TEST_COMMAND, (error, stdout, stderr) => {
        if (error !== null) {
            return console.error('speedtest exec error: ' + error);
        }
        console.log('stdout: ' + stdout);
        onExecSpeedTest(stdout);
    });
}

function onExecSpeedTest(json) {
    if(!json) {
        return;
    }
    const jsonObject = JSON.parse(json);
    if(jsonObject && jsonObject.download) {
        console.info(jsonObject);

        const BAND_TO_KBIT = (jsonObject.download.bandwidth * 8) / 1024;
        console.info(`speed test result: ${(BAND_TO_KBIT / 8) / 1000}MB/s`);
        const THROTTLE_KBIT = Math.floor(BAND_TO_KBIT * THROTTLE_RATIO);
        execSteam(THROTTLE_KBIT);
    } else {
        console.error("なんかJSONのエラー");
    }
}

function execSteam(speed) {
    const EXEC_STRING = `${STEAM_EXE} ${STEAM_THROTTLE_PARAM} ${speed}`;
    console.log(EXEC_STRING);

    exec(EXEC_STRING, (error, stdout, stderr) => {
        if (error !== null) {
            return console.error('steam exec error: ' + error);
        }
        console.info(`set download throttle: ${(speed / 8) / 1000}MB/s`);
    });
}

execSpeedTest();
```

`execSpeedTest`内で`speedtest.exe`を呼び出します。`-f json`とすることで、JSONの文字列が取得できます。

`onExecSpeedTest`内で取得できたJSONをパースし、その中にある帯域の情報を取得して、その時の帯域の0.8倍のスピードを帯域制限の値としています(bytesで格納されていますが、Steamの帯域制限はKBpsで指定するため変換処理も行っています)

`execSteam`でspeedを受け取って、Steam.exeに対して`Steam.exe +set_download_throttle <帯域制限速度>`を指定してあげます。

```node index.js```

の実行(またはnpm run startなどに指定)をすることで無事に手元の環境では帯域制限を行うことができました！

## 感想

思い付きで始めたのですが、Speedtest CLIが使いやすかったりSteamの設定も容易で、非常に助かりました

最初Steamフォルダ内のconfigファイルを直接書き換えたりしてみたのですが、それだとクライアントを再起動しないと帯域制限がかからなかったりしていろいろと調べました。

何度かSpeedtestを行った平均値を求めてからやるとか、定期的に実行するとかすればより確実性が増すかと思います。