Skip to content

ぺやんぐがLabで開発してるときに作ったユーティリティとかをまとめたやつ。

License

Notifications You must be signed in to change notification settings

TeamKun/PeyangPaperUtils

Repository files navigation

PeyangPaperUtils

ぺやんぐがLabで開発してるときに作ったユーティリティとかをまとめたやつ。


注意

  • このライブラリを同梱するときは、必ずパッケージのリロケートをしてください。

使い方

  1. あなたのプラグインのonEnable()に、以下を追加します:
  PeyangPaperUtils.init(this);
  1. あなたのプラグインのonDisable()に、以下を追加します:
  PeyangPaperUtils.dispose();

いまあるもの


Runner

BukkitRunnableの匿名クラスでタスク作るのが面倒くさい人向けのユーティリティ。
まあつまり, BukkitRunnableをプラグイン指定無しで使えるってやつ。
あ, あと, Runner.run系のメソッドのラムダは例外を許容する。例外をキャッチするには, (大体) 第ニ引数に例外をキャッチするラムダをつっこむ。

  Runner.run(() => {
      Terminal.ofConsole().info("Hello, world!");
  });
  
  // 1秒毎(20チック)に実行
  Runner.runTimer(() => {
      Terminal.ofConsole().info("Hello, world!");
  }, 20L);
  
  // 1秒毎(20チック)に実行 + カウント
  Runner.runTimer((long count) => {
      Terminal.ofConsole().info(count + ": Hello, world!");
  }, 20L);
  
  Runner.runTimer(() => {
      throw new IOException("Hello, exception!");
  }, (Exception exception, BukkitTask task) => {
      Terminal.ofConsole().error(exception.getMessage());
      task.cancel();
  }, 20L);

ターミナルシステム

プレイヤとコンソールのI/Oを統一するインタフェースとユーティリティをまとめたやつ。
↓↓↓ターミナルを初期化↓↓↓

  Player examplePlayer;
CommandSender sender;
  
  Terminal terminal = Terminals.of(this.examplePlayer);
  // or
  Terminal terminal = Terminals.of(this.sender);
  // or
  Terminal terminal = Terminals.ofConsole(); 

ログ出力

ログをめちゃくちゃ簡単に吐けるやつ..!!!

  terminal.info("Hello World!");  // 青色で I: Hello World! と出力
  terminal.warn("Hello World!");  // 黄色で W: Hello World! と出力
  terminal.error("Hello World!"); // 赤色で E: Hello World! と出力
  terminal.success("Hello World!"); // 緑色で S: Hello World! と出力
  
  terminal.writeLine("Hello World!"); // Hello World! と出力
  terminal.write(TextComponent.of("Hello World!")); // Hello World! と出力 (Adventure API)

プログレスバー(いまはプレイヤのみ)

プログレスバーを表示するやつ。文字通り。

  ProgressBar progress = terminal.createProgressbar("example_progressbar");
  progress.setProgressMax(100);
  progress.setPrefix("Progress: ");
  progress.setSuffix(" %");
  progress.show();
  // 別スレッドで実行
  for(
int i;
i< 100;i++){
      progress.setProgress(i);
      Thread.sleep(100);
  }
  
  // バリアで待機てきな? ことを? しとく??
  
  progress.hide();

入力システム

プレイヤ/コンソールに質問を行い, 入力を受け付ける機能。
質問の返答は

  • プレイヤ: チャット (prefix/suffixなし)
  • コンソール: コンソール(コマンド実行する感覚) で行できる。
  Input input = terminal.getInput();

QuestionResult result = this.input.showYNQuestion("Are you sure?").waitAndGetResult();
  if (result.test(QuestionAttribute.YES))
      terminal.info("Yes, it is.");
  else
      terminal.info("No, it isn't.");
  
  result = input.showYNQuestionCancellable("Are you sure?", QuestionAttribute.APPLY_FOR_ALL).waitAndGetResult();
  if (result.test(QuestionAttribute.YES))
      terminal.info("Yes, it is.");
  else if (result.test(QuestionAttribute.CANCEL))
      terminal.info("Cancelled.");
  else
      terminal.info("No, it isn't.");
  
  if (result.test(QuestionAttribute.APPLY_FOR_ALL))
      terminal.info("Applied for all.");

警告

  • waitうんたら系は, 非同期で使ってください。下手したらメインスレッド止めてサーバ死にます。いや。マジで。

コマンドシステム

コマンドとか簡単に作ったり, 引数のバリデーションを簡単にできるユーティリティ郡。
コマンドのヘルプを自動で構築する。(/<コマンド名> help [ページ番号] で使用) サブコマンドを使用する場合 SubCommandable を継承する。

plugin.yml
  name: ExamplePlugin
  
  commands:
    examplecommand:
      aliases:
        - ex
  permission:
    examplepermission:
      default: op
CommandManagerを管理するクラス(onEnableがあるとこ推奨)
  CommandManager manager;

  // onEnable 内

  this.manager = new CommandManager(this, "examplecommand", "ExamplePlugin", "examplepermission");
  
  this.manager.registerCommand("dostuff", new CommandDoStuff(), "ds", "do_stuff", "stuff");
コマンドクラス
public class CommandDoStuff extends CommandBase
{

  @Override
  public void onCommand(@NotNull CommandSender sender, @NotNull Terminal terminal, String[] args)
  {

    if (this.indicateArgsLengthInvalid(terminal, args, 1, 2)  // 引数の長さが1~2でない場合はエラーを表示して終了
            || this.indicatePlayer(terminal)  // コンソールから実行された場合はエラーを表示して終了
    return;

    String stuffName = args[0];
    Integer repeatCount;
    if (args.length >= 2 && (repeatCount = this.parseInteger(terminal, args[1], 1, 100)) == null)
      // 引数が2つ以上で, 2番目の引数が1~100の整数でない場合はエラーを表示して終了
      return;
    else
      repeatCount = 1;

    for (int i = 0; i < repeatCount; i++)
      terminal.info("Do stuff: " + stuffName);

  }

  @Override
  @Nullable
  public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Terminal terminal, String[] args)
  {
    return List.of("stuff1", "stuff2");
  }

  @Override
  @Nullable
  public String getPermission()
  {
    return "exampleplugin.dostuff";
  }

  @Override
  public TextComponent getHelpOneLine()
  {
    return of("Do stuff!");
  }

  @Override
  public String[] getArguments()
  {
    return new String[]{
            this.required("stuff"),  // 必須引数
            this.optional("repeat")  // 任意引数
    };
  }
}

コンフィグシステム

ファイルベースのコンフィグを簡単に作れる。

@Getter
class ConfigClass
{
  @Config("Example Value 1")
  private final String exampleValue1 = "example1";
  @Config("Example Value 2", min = 1, max = 10)
  private final int exampleValue2 = 5;
  @Config("Example Ranged Value", min = 1, max = 10, ranged = true)
  private final int minExample = 1;
  @Config("Example Ranged Value", min = 1, max = 10, ranged = true)
  private final int maxExample = 10;
  @Config("Example string enum", enums = {"example1", "example2"})
  private final String exampleStringEnum = "example1";
}

  ConfigManager<ConfigClass> manager = new ConfigManager<>(Paths.get("config.json"), ConfigClass.class);

サウンドセット&サウンドユーティリティ

たぶんつかうであろうサウンドを予め定義したEnumたち!!!
サウンドを定義するEnum用の便利なインタフェース!!!

  Player player;
  
  // クエスト開始時とか?
  QuestSoundSet.QUEST_START.play(player);
  
  // プレイヤリスポーン時とか?
  PlayerSoundSet.RESPAWN.play(player);
  
  @Getter
  enum MySoundSet implements SoundSet
  {
      MY_SOUND_1(Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f),
      MY_SOUND_2(Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 1.0f),
    
      ;
      
      private final Sound sound;
      private final SoundCategory source;
      private final float volume;
      private final float pitch;
  }
  
  MySoundSet.MY_SOUND_1.play(player, PlayArea.NEARBY_10);  // 付近10m以内のプレイヤのみに再生
  MySoundSet.MY_SOUND_2.play(player, 1.0f, 0.5f);  // ボリューム1.0, ピッチ0.5で再生
  // その他, サウンドの再生位置を指定したり, サウンドカテゴリを指定したり, サウンドの再生範囲を指定したりできるやついっぱい。
  // 多分全部網羅してるはず(してなかったらIssue or PR or なんかで教えてください)

DBのトランザクションシステム

  Connection connection;
  class Employee {
      int id;
      String name;
      int age;
      String type;
  }

QueryResult<Employee> result = Transaction.create(this.connection, "SELECT * FROM example_table WHERE id = ? AND type = ?")
      .set(1, 123456)
      .set(2, "EMPLOYEE")
      .executeUpdate();
  • DB操作時の自動コミット(すべての実行の最後にコミット)
      Transaction.create(connection)
          .doTransaction((Transaction) -> {
              Connection connection = transaction.getConnection();
              // ここでDB操作
          });
  • DB操作時のtry-catchを省略できるように!(非チェック例外にします) => SQLExceptionが発生した場合は自動的にロールバック!!
  • close忘れがなくなる
  • 結果をStream処理できる
      result.stream().map((ResultRow row) -> {
          Employee employee = new Employee();
          employee.id = row.getInt("id");
          employee.name = row.getString("name");
          employee.age = row.getInt("age");
          employee.type = row.getString("type");
          return employee;
      }).forEach((Employee employee) -> {
          System.out.println(employee.name);
      });
  • 結果をオブジェクトに変換して取得(マッパーを予め定義)
      result.mapper((ResultRow row) -> {
          Employee employee = new Employee();
          employee.id = row.getInt("id");
          employee.name = row.getString("name");
          employee.age = row.getInt("age");
          employee.type = row.getString("type");
          return employee;
      });
    
      while (result.next()) {
          Employee employee = result.get();
          System.out.println(employee.name);
      }
  • 結果をListで取得(マッパーを引数に突っ込む)
      ArrayList<Employee> employees = result.mapToList((ResultSet resultSet) -> {
          Employee employee = new Employee();
          employee.id = resultSet.getInt("id");
          employee.name = resultSet.getString("name");
          employee.age = resultSet.getInt("age");
          employee.type = resultSet.getString("type");
          return employee;
      });

Component API のテキスト拡張

v3.0.0 より Adventure API の内部仕様を廃止した。
これにより, PaperMC 1.16.5 以下でも使用できるようになったとともに, 一部の機能が削除された。

これには, Adventure API より提供されていた TextComponent も含まれるため, 同等の機能を持つ API を作成する必要があった。 v3.0.0 より作成された Text は Bungeecord による Component API を拡張し、より簡単かつ高度に使用できる API を提供する。

Text text = Text.of("Hello, world!")
    .color(ChatColor.AQUA)  // Component API 系および Bukkit の ChatColor に対応
    .bold()
    .runCommandOnClick("/say Hello, world!")  // / の有無は自動で判定し適切に追加
    .hoverText("This is hover text.")
    .append(Text.of("This is appended text!")
        .hoverText(Text.of("This is appended hover text.").color(ChatColor.GRAY))
        .color(ChatColor.RED)
    .append("This is appended text without Text.of().")
   
Terminal terminal = Terminals.ofConsole();
terminal.write(text);