MODメンテナンスの事とか

最近は1日1時間ぐらいMODのメンテナンスをやってます。
と言っても殆どは最適化で、それも体感できるような物は少ない感じのコード。

今日はブロックを叩いたときのパーティクルが出ない事に気付いたからコードを見てみると・・・
パーティクルの座標がずれていましたw

このパーティクルは本体のコードaddBlockHitEffectsから貰っているんだけれど、これものすごく無駄があるんですよ

public void addBlockHitEffects(int p_78867_1_, int p_78867_2_, int p_78867_3_, int p_78867_4_) {
    Block block = this.worldObj.getBlock(p_78867_1_, p_78867_2_, p_78867_3_);
 
    if (block.getMaterial() != Material.air) {
        float f = 0.1F;
        double d0 = (double)p_78867_1_ + this.rand.nextDouble() * (block.getBlockBoundsMaxX() - block.getBlockBoundsMinX() - (double)(f * 2.0F)) + (double)f + block.getBlockBoundsMinX();
        double d1 = (double)p_78867_2_ + this.rand.nextDouble() * (block.getBlockBoundsMaxY() - block.getBlockBoundsMinY() - (double)(f * 2.0F)) + (double)f + block.getBlockBoundsMinY();
        double d2 = (double)p_78867_3_ + this.rand.nextDouble() * (block.getBlockBoundsMaxZ() - block.getBlockBoundsMinZ() - (double)(f * 2.0F)) + (double)f + block.getBlockBoundsMinZ();
 
        if (p_78867_4_ == 0)
        {
            d1 = (double)p_78867_2_ + block.getBlockBoundsMinY() - (double)f;
        }
 
        if (p_78867_4_ == 1)
        {
            d1 = (double)p_78867_2_ + block.getBlockBoundsMaxY() + (double)f;
        }
 
        if (p_78867_4_ == 2)
        {
            d2 = (double)p_78867_3_ + block.getBlockBoundsMinZ() - (double)f;
        }
 
        if (p_78867_4_ == 3)
        {
            d2 = (double)p_78867_3_ + block.getBlockBoundsMaxZ() + (double)f;
        }
 
        if (p_78867_4_ == 4)
        {
            d0 = (double)p_78867_1_ + block.getBlockBoundsMinX() - (double)f;
        }
 
        if (p_78867_4_ == 5)
        {
            d0 = (double)p_78867_1_ + block.getBlockBoundsMaxX() + (double)f;
        }
 
        this.addEffect((new EntityDiggingFX(this.worldObj, d0, d1, d2, 0.0D, 0.0D, 0.0D, block, this.worldObj.getBlockMetadata(p_78867_1_, p_78867_2_, p_78867_3_))).applyColourMultiplier(p_78867_1_, p_78867_2_, p_78867_3_).multiplyVelocity(0.2F).multipleParticleScaleBy(0.6F));
    }
}

無駄ってのは第4引数p_78867_4_を評価しているところ。
p_78867_4_は叩かれた面の数字が入るのだけれど、途中で値が変動しないのにも関わらず6面全部を評価しているところ。
まあ、これはデコンパイルされたコードだからこうなってるだけで、本当のコードは違うのかもしれないけどw

レンダリングに関わるところなので1秒間に何度も呼ばれる物だからswitchの方が高速。
なのでうちのコードはこんな感じ

public boolean addHitEffects(World world, MovingObjectPosition target, EffectRenderer effectRenderer) {
    float range = 0.1F;
    double pX = (double)target.blockX + world.rand.nextDouble() * (this.getBlockBoundsMaxX() - this.getBlockBoundsMinX() - (double)(range * 2.0F)) + (double)range + this.getBlockBoundsMinX();
    double pY = (double)target.blockY + world.rand.nextDouble() * (this.getBlockBoundsMaxY() - this.getBlockBoundsMinY() - (double)(range * 2.0F)) + (double)range + this.getBlockBoundsMinY();
    double pZ = (double)target.blockZ + world.rand.nextDouble() * (this.getBlockBoundsMaxZ() - this.getBlockBoundsMinZ() - (double)(range * 2.0F)) + (double)range + this.getBlockBoundsMinZ();
 
    switch (target.sideHit) {
        case 0:
            pY = (double)target.blockY + this.getBlockBoundsMinY() - (double)range;
            break;
        case 1:
            pY = (double)target.blockY + this.getBlockBoundsMaxY() + (double)range;
            break;
        case 2:
            pZ = (double)target.blockZ + this.getBlockBoundsMinZ() - (double)range;
            break;
        case 3:
            pZ = (double)target.blockZ + this.getBlockBoundsMaxZ() + (double)range;
            break;
        case 4:
            pX = (double)target.blockX + this.getBlockBoundsMinX() - (double)range;
            break;
        case 5:
            pX = (double)target.blockX + this.getBlockBoundsMaxX() + (double)range;
            break;
    }
 
    int meta = 0;
    try {
        TileEntity_Multibase te = (TileEntity_Multibase)world.getTileEntity(target.blockX, target.blockY, target.blockZ);
        meta = te.getMeta();
    } catch (Exception e) {
    }
 
    effectRenderer.addEffect((new EntityDiggingFX(world, pX, pY, pZ, 0.0D, 0.0D, 0.0D, this, meta, target.sideHit)).applyColourMultiplier(target.blockX, target.blockY, target.blockZ).multiplyVelocity(0.2F).multipleParticleScaleBy(0.6F));
 
    return true;
}

メソッド名や引数が違うのはコードを使ってる場所が違うだけで内容は全く同じ。
本体のコードと違うところは、叩かれた面によって座標を変動させる所。
さっきも書いたけど、1秒間に何回も呼ばれるメソッドだし、変動しない値を6回も評価するのは無意味なのでswitchで目的の値へ飛ぶ方が速い。

でもまあ、こんな感じで本体のコードから持ってきたヤツも信用せずちゃんと書き換えれば少しは体感できるぐらい軽くなるのかも?

Selectable Paintingsが進まないのもこの辺があってすごく面倒だから・・・
テクスチャの解析に時間が掛かるからって言って当時にできる限りの最適化をやってたからなあー。

資源用ディメンションを追加するMODを公開

うちのサーバーで使っていた資源用ディメンションを追加するMODを一般公開しました。

カスタムディメンションを追加するMODは幾つかあったけれど、重かったり不具合が多かったりアップデートチェックがOFFにできず、応答が遅いサーバーのお陰で不安定になったり・・・
ポータルを作らず単純にディメンションを追加するMODってないんですよね。
なので、作ったって感じですw

機能としては、ディメンションを1~2追加しディメンションを行き来できるテレポーターアイテムを追加するだけ。
ディメンションの地形生成タイプをデフォルト、フラット、ラージバイオーム、アンプリファイド、ネザー、エンドと指定できてシードの個別設定も可能。
エンドはおまけ・・で追加した程度の物なので、運が悪いと奈落へドボンw

テレポーターは使用した時点のワールドと座標を記録し、資源ワールド以外なら資源ワールドへ、資源ワールドなら資源ワールド以外で使用された座標へテレポートできます。
なので、通常世界の倉庫でテレポーターを使用して資源ワールドへ移動、そのあと資源ワールドから再度テレポーターを使うと通常世界の倉庫へ戻ってこれるって感じです。
更にテレポーターを使用すると、前回資源ワールドでテレポーターを使った地点に移動できます。

全然移動していないけれど、ちょっとした動画。

一応、向いている方向も記録されるのでどっちの方向に進んでいたか・・もわかると思いますw
結構便利テレポーターなのです。

資源用ワールドなので、サーバーを止めてディメンションのフォルダを削除すればまた再生成可能です。

需要があれば使ってみてくださいー

Minecraft v1.7.10用MOD公開!

前回の更新から1年ぐらい開いちゃいましたけど・・Minecraft v1.7.10に対応しました。

今回対応したMODは
 Halogen Light、Animal Blocks、Marble Blocks、Fullcolor Blocks、More Fences、Fence Slab(新規MOD)

今後1.7.10への対応予定があるMODは
 Selectable Paintings、Country Filter

今回から更新を停止したMODは
 Column、Multislab、More Material Blocks
って感じになります。

もう知ってる方は多いと思いますが、全MODのバージョンを初期化しました。
これは移植の際にコードが変わってる部分が多かったので、位置から作り直しちゃえ!ってことでコードを書き直したからです。
もちろん全く移植無しではないんですけどねw

あとv1.6.4まではMOD毎にTileEntityを登録していたのですけど、今回はTileEntityを見直して前提MODで登録し全てのMODを同じTileEntityで管理するようにしました。
MODを抜いたときにログにでる「TileEntityをスキップした」ってうざいログはでません。
前提MODを抜けばでるかもだけど・・w

あと若干Javaの本を読み直したのでコーディングする時も常に最適に動くようにしてみました。
例えば前回のString+Stringの連結だったり、数字のif-elseが3回以上続くならswitchの方が軽量で高速であるとか、for(int i=0;i<array.length; ++i)よりfor(int i=0, max=array.length;i<max; ++i)の方が高速である等・・・
ちゃんと読めば色々書いてあるんですよねー

今後は気付いた範囲でコードを最適化して少しでも軽く軽快に動くように作ってみようと思います。

Minecraft v1.7.10 MODほぼ完成

Minecraft v1.8.xから作るつもりだったけれど1.7.10で作ってしまった。
全部ブロック関連のMODなのでv1.6.4から楽に移植できました。
今の所、Selectable Paintingsは全く手を付けていないのでどれぐらい変更が必要か不明って感じです。

移植したのはAnimal Blocks、Marble Blocks、Halogen Lightの3つ。
Minecraft v1.7.10 - Kerberos MODs 1

独自の前提MODが必要なのは以前と変更ありませんが、内部的には全然変わってきています。
以前の前提MODは単純にパケット関連と共通のキー管理だけでしたけれど、今回はブロックのベース部分もあります。
TileEntityを汎用性の高い物にしたので1つのTileEntityで複数のMODを管理できるのもよさげ。

Halogen Lightはレシピのコストを軽減し、Marble BlocksはモミジMODが導入されていればモミジMODの大理石から作成可能になるようにしました。
あと新しいMODでフェンス用半ブロックを追加するMOD、Fence Slabを用意しました。
Minecraft v1.7.10 - Kerberos MODs 2
これはdefeatedcrow氏のMODからコードを頂きました。

あと久々に読み直したJavaの本にStringの連結にはString+StringよりStringBuilderの方が低メモリ消費&高速である事が書かれていました。
少ない文字連結でも威力を発揮するそうです。

String a = "my name is ";
String b = "lambert";
String c = a + b;

より

StringBuilder s = new StringBuilder("my name is ");
s.append("lambert");
String c = s.toString();

のほうが良いと言うこと。
今まで普通に前者で連結してたよ・・・w

レンダリング時のfpsの分だけ呼ばれるItemクラスのaddInformationメソッドなんかは効果バツグンだろうなー
少しでも低負荷なMODを作ってみようと思います。(TileEntity乱用してる時点で矛盾してるわけだけど・・w)

と言うことで、色々最適化や細かな修正をして煮詰めていけば公開できそうです。

AppleMilkTea2のアドオンを作ってみる

AppleMilkTea2等のMODをいれて遊んでいるMinecraft。
Minecraft 1.7.1.0 - DQRでプレイ中 Minecraft 1.7.1.0 - DQRでプレイ中
りんごの需要が出てきたけれど、リンゴの入手数が追いつかないので独自MODでリンゴの木を追加することにしました。

AppleMilkTea2はAPIも公開されているし、専用アドオンのチュートリアルもあるので参考にして作っていきます。
Minecraft 1.7.1.0 - AppleMilkTea2 アドオン制作中
コードは柚子の木・葉・苗をベースにするだけなので楽ちん。

Minecraft 1.8でSelectable Paintingsを作り直すと言って全くやってなかったので、ブロック追加や地形生成なんかを弄ったら知らないメソッド、無くなったメソッドがいっぱいあって大変でしたw
1.6までは大変だったTileEntityや独自Entity等のパケットもかなり簡単に使えるようになっているので1.7からのMOD製作は楽で良い (`・ω・)b

そんなこんなでリンゴの木が完成。
Minecraft 1.7.1.0 - AppleMilkTea2 アドオン りんごの木
テクスチャも柚子を流用。

しかし・・・AppleMilkTea2のいろんな植物に使用されている右クリック収穫。
これすごい便利ですねー、葉を右クリックするだけで収穫できちゃうんだからw