$.onStart(() => {
  $.state.players = {};
});

$.onUpdate((deltaTime) => {
  let players = $.state.players;
  const currentPlayers = $.getPlayersNear(new Vector3(0, 0, 0), Infinity);

  // 既存のリストにないプレイヤーを新しいプレイヤーとして取得
  const newlyJoinedPlayers = currentPlayers.filter(p => !players[p.id]);

  // 新しいプレイヤーがいれば更新フラグをオンにする
  let playersChanged = newlyJoinedPlayers.length > 0;

  for (const player of newlyJoinedPlayers) {
    if (player.exists()) {
      // 新しいプレイヤーにPlayerScriptを設定
      $.setPlayerScript(player);

      // 自身をプレイヤーリストに追加
      players[player.id] = {
        group: 0,
        player: player
      };
    }
  }

  const beforeCleanupCount = Object.keys(players).length;

  // 退室したプレイヤーを削除
  players = Object.fromEntries(
    Object.entries(players).filter(([key, value]) => value.player.exists())
  );

  // 退室したプレイヤーがいた場合も更新フラグをオンにする
  playersChanged ||= Object.keys(players).length !== beforeCleanupCount;

  // プレイヤーリストを保存
  $.state.players = players;

  // 変更がなければ何もしない
  if (!playersChanged) return;

  // 変更があった場合のみ全員にプレイヤーリストを送信
  Object.values(players).forEach(value => {
    value.player.send("players", players);
  });
});

$.onReceive((type, arg, sender) => {
  if (type === "group" && sender instanceof PlayerHandle) {
    // 対象プレイヤーの会話グループを更新
    const players = $.state.players;
    players[sender.id].group = arg;
    $.state.players = players;

    // 変更を通知
    Object.values(players).forEach(value => {
      value.player.send("players", players);
    });
  }
}, { player: true });