【Minecraft】UHCを作ろう!#1

始める前に

以下のことが最低限できるものとする

・サーバー構築、プラグイン導入

・Javaの基本的な構文

・EclipseなどのIDEでプラグインを作成できる

 

UHC とは 「Ultra Hardcore Champion」の略である。

ゲーム説明

大手サーバーでは最大70人。装備を揃えて戦うゲーム。

満腹ゲージによる自動再生はなく、金リンゴを食べる、敵を倒したときに落とす頭を使う、回復系ポーションの使用などを使用しないと一切回復が行えないのが、このゲーム最大の特徴である。

ゲームの流れ

・ゲーム開始。ランダムな場所にスポーンする。

・10分後 PVPが許可され、敵にダメージを与えることができる。

・残り15人になると、デスマッチまで10分になる。

・10分後 全員一定の場所にテレポート。残り一人になると試合終了。デスマッチが始まって15分経過した場合はキル数が最多のプレイヤーの勝ちとなる(最多同数のプレイヤーがいた場合はDraw)。

UHCの使用(Wikiより参照)

  • 満腹ゲージが一定以上の場合でも体力が自然回復しない。回復するには金リンゴ、ポーション、プレイヤーの頭などが必要。
  • 金リンゴの回復する量が通常の2倍(4♡+衝撃吸収2♡)
  • プレイヤーを倒すと頭(Head)をドロップする。その頭を右クリックすることでチームの全員が再生エフェクトで3♡回復できる。(Soloでは4♡)
  • 即時回復のポーションの回復量が異なる。(Ⅰ:4♥、Ⅱ:8♥)
  • 毒のスプラッシュポーションの効果時間が異なる。(Ⅰ:33秒、Ⅱ:5秒)
  • 力のポーションの効果は通常の62%に弱体化されている。
  • 移動速度上昇の効果量が変更されている。(Ⅰ:+20%、Ⅱ:+30%、Ⅲ:+45%)
  • ガストの涙を使って再生のポーションは醸造することはできない。
  • プレイヤーのHPが40(20♡)。
  • 最初の準備期間は、全てのプレイヤーに火炎耐性の効果がついている。
  • 通常より満腹度の減りがかなり少ない。
  • ワールドボーダーがある。Teamだと+-1500,Soloだと+-1000からPvPが有効になると徐々に狭くなっていく。
    • ワールドボーダーの外にいると窒息しているかのようにダメージを受ける
  • ワールドの真ん中周辺 (X.0 Z.0から直径約100m)が開けた土地になっている。
  • 海洋バイオームが存在しない(サバンナバイオームになる)。
  • 常に昼である(デスマッチ会場は夜にもなり、MOBが湧く)。
  • 砂利を壊したときに25%で火打石をドロップする(通常とは確率が違う)。
  • MOBがドロップするアイテムが多少異なる。これについては後述。
  • MOBからの経験値が通常の2倍。
  • ハサミでヒツジの毛を刈ると33%の確率で糸を1本ドロップする。
  • 子供の馬は砂糖を上げると大人に成長する
  • ネザーでは、ネザー要塞以外でもブレイズがスポーンする。(バグで通常世界のモブが沸くことがある)
  • 通常とは違う様々な特殊レシピがある。コインを使ってショップで解放できる。
  • きらめくスイカの作り方が異なる。スイカを中央に、金インゴットを上下左右に1個ずつ計4つ置いてクラフトする。 画像
  • Teamモードでは、ゲームが始まる前の待機時間に /team invite (プレイヤー名)で他のプレイヤーをチームに誘い、/team accept (プレイヤー名)で自分を招待したプレイヤーのチームに参加できる。
  • Tabキーを押すとプレイヤー一覧と、各自のHPが表示されるが、そこには金ハート分のHPが加算されている。
    • 弓、釣り竿などを敵に当てた際にHPが表示されるが、それには金ハートは加算されてないので注意)。
  • 糸を使って弓を金床で修理できる。レベルは使用しない。
  • エンチャントテーブルでは幸運のエンチャントが付与されない。
  • 鉱石の生成量が増加している。
  • xray対策のため、鉱石はプレイヤーが一定距離内に入ると表示される。
    • 鉱石が表示されているのに、破壊しても鉱石がドロップしないバグが時々起きる。

これ以上の詳細な説明は以下のWikiを参照してもらいたい。

Wiki

サーバー準備

パート1から早速作っていこうと思う。

だがそのためには、初めにサーバーを用意しなければならない。

今回はSPIGOTの 1.8.8を使用する。

プラグイン開発はSPIGOT(Bukkit)ベースなのでBukkit派生であればなんでもかまわない(例えばPaper)

 

さて、サーバー(API)の準備ができたところで、プラグインを作っていこう。

EclipseなどのIDEを立ち上げてプロジェクトを作成して、ビルドパスにAPIを通そう。

次にメインとなるクラスを作成しよう。

今回は「Game」というクラスを作成した。

中身は何もない。

この「Game」というクラスがプラグインの基準というか、中心となる。なるべく完結に綺麗にコードをまとめて見やすくする努力をしよう。

onEnable と onDisable を作成する。

「static Game Instance;」を作成して、onEnable ないに 「Instance = this;」 

onDisable ないに 「Instance = null」を追記。

onDisable は必須ではないが、Javaの性質上 static で定義した変数は必ずメモリを解放してあげてください。

プログラムが終了するまでメモリを解放されず、確保したままになってしまいます。

次のパートではイベントリスナーを実装します。

【Bukkit】【Spigot】リーチハックの対策の方法

リーチハック、意外とたちが悪いハックです。

これに遭遇したら攻撃が当たりません!

エンティティがダメージを受けた時にそのソース元(プレイヤー)からの距離を計算し、規定の値以上の時はリーチハックとして警告するプラグインを作ってみます。

対策方法

エンティティがダメージを受けた時のイベント

EntityDamageByEntityEventを使用します。

// 最大のリーチ
public static double final MAX_REACH = 3.45D;

@EventHandler
public void onDamage(EntityDamageByEntityEvent event){
// 攻撃元がプレイヤーであるか
if (event.getDamager() instanceof Player) {
// プレイヤーであったとき、
// 対象との距離を計算する
double range = event.getDamager().getLocation().distance(event.getEntity().getLocation());
if (range > MAX_REACH) {
// 最大のリーチを超えてます
Bukkit.broadcastMessage("Reach !!!");
}
}
}

このコードは高低差を考慮していないものになります。

高低差を考慮するには以下のようなコードを加える必要があります。

// 高低差の確認
double rangeY = Math.abs(event.getDamager().getLocation().getY() - event.getEntity().getLocation().getY());

if (rangeY > MAX_REACH) {
// これはハック
} else {
// 単純な距離を計算する
}

【Bukkit】【Spigot】スピードハック対策の方法

スピードハックは最もよく知られているハックであり、一番対策がしやすいのかなとも思います。

 

プレイヤーの動きを検知する

Bukkit プラグインは

イベントの発火

プラグイン側で処理

という流れとなっており、プレイヤーが動いた時の専用のイベントがあります。

そのイベントを使用してハックかどうかの検査を行います。

@EventHandler
public void onMove(PlayerMoveEvent event){
// ここで処理を行います
}

 

しかしこのままだと顔が動いただけでもイベントが発火するため移動していなくても処理が行われてしまいます。

それを防ぐために

if (event.getFrom().getBlockX() != event.getTo().getBlockX()
|| event.getFrom().getBlockY() != event.getTo().getBlockY()
|| event.getFrom().getBlockZ() != event.getTo().getBlockZ()){
// ここで処理を行う
}

↑のコードを追加することで、X、Y、Zのいずれかが動いた時(移動したとき)に処理を行うことができるので、多少ラグを軽減できると思います。

※重い処理を行う際は非同期で処理しましょう

 

さて本題の処理の部分です。

初めにプレイヤーのダッシュ時の速度を変数として保存しておきます。

※ないと思いますが、バージョンアップで移動速度が変わるかもしれないので

public static double final MAX_SPEED = 0.666D;

次にプレイヤーの移動距離を計算し、ダッシュ時の速度を超えている場合はロールバック処理を行います。

// 移動距離の計算
if (event.getFrom().distance(event.getTo()) > MAX_SPEED){
// ダッシュ時の速度を超えているので
// ロールバック処理を行う
event.setCancelled(true);
// ※イベントをキャンセルすることで移動できなくなる
}

最後に全体のソースコードになります。

public static double final MAX_SPEED = 0.666D;
@EventHandler
public void onMove(PlayerMoveEvent event){
if (event.getFrom().getBlockX() != event.getTo().getBlockX()
|| event.getFrom().getBlockY() != event.getTo().getBlockY()
|| event.getFrom().getBlockZ() != event.getTo().getBlockZ()){
if (event.getFrom().distance(event.getTo()) > MAX_SPEED){
event.setCancelled(true);
}
}
}

 

このコードは完全ではないです。

例えば、シフトをしているときやクモの巣、水の中にいるとき、など細かく条件を加えていくことでより詳細にハックかどうかを確認しなければいけません。

また、PVPサーバーでこのコードを使うと、ロールバックを沢山して動けなくなる可能性があります。

なぜなら殴られた時のノックバックによって移動速度(ベクトル)が加算され、MAX_SPEEDを超えてしまうためです。

このコードは改良の余地がたくさんあります。

ぜひこのコードを参考にして独自の対ハックプラグインを作ってみてください。