sumisoクラフト

sumisoクラフトはてなブログ拠点

sumiso専用スクリーンセーバーを作る

今週のお題「懐かしいもの」

youtu.be

スクリーンセーバーを作成しました

スクリーンセーバーとは

ChatGPTより

コンピュータのディスプレイの焼き付き(画面に表示内容が長時間固定されることによって生じる残像)を防ぐために、一定時間操作が行われなかったときに自動的に表示されるプログラムのことです

しかし、現代のディスプレイは性能が向上し、焼き付きが起こらなくなったため
スクリーンセーバーが利用されることはなくなりました
今や懐かしの技術、です

経緯

ある日Qiitaのトレンドに乗っていたこちらの記事

qiita.com

スクリーンセーバーwindows formアプリで簡単に作れるそうです
という訳で冒頭のスクリーンセーバーを作成しました

環境

機能概要

  • windows formアプリ
  • 全画面表示
  • マウス非表示
  • ロゴ画像アニメーション
  • マウス入力で終了
  • キー押下で終了
  • サブモニタにも適当なフォーム表示
  • 多重起動防止

実装

ChatGPT 3.5でコード生成し Google検索で機能を探し
ツギハギで動かしました

Program.cs

namespace SumisoScreenSaver
{
    internal static class Program
    {
        /// 
        ///  The main entry point for the application.
        /// 
        [STAThread]
        static void Main()
        {
            // To customize application configuration such as set high DPI settings or default font,
            // see https://aka.ms/applicationconfiguration.
            
            //Mutex名を決める
            string mutexName = "sumiso craft ScreenSaver";
            //Mutexオブジェクトを作成する
            bool createdNew;
            System.Threading.Mutex mutex =
                new System.Threading.Mutex(true, mutexName, out createdNew);

            //ミューテックスの初期所有権が付与されたか調べる
            if (createdNew == false)
            {
                //されなかった場合は、すでに起動していると判断して終了
                mutex.Close();
                return;
            }

            try
            {
                ApplicationConfiguration.Initialize();
                Application.Run(new Form1());
            }
            finally
            {
                //ミューテックスを解放する
                mutex.ReleaseMutex();
                mutex.Close();
            }
        }
    }
}

プログラムのエントリポイント
ほぼ

dobon.net

のコピペです

Form1.cs

namespace SumisoScreenSaver
{
    public partial class Form1 : Form
    {
        private Image image = Resource1.design_01;
        private Point position;
        private Point speed;

        private int imageWidth = 0;
        private int imageHeight = 0;

        private Point lastMousePosition = new Point();

        public Form1()
        {
            InitializeComponent();
            InitializeAnimation();
        }
        private void InitializeAnimation()
        {
            this.DoubleBuffered = true; // フリッカーを防ぐための設定
            this.FormBorderStyle = FormBorderStyle.None; // フォームの枠を消す
            this.WindowState = FormWindowState.Maximized; // フォームを最大化
            this.TopMost = true; // 常に最前面に表示
            Cursor.Hide();

            // 画像表示サイズ

            var scale = 1.5;

            imageHeight = (int)(image.Height / scale);
            imageWidth = (int)(image.Width / scale);

            // 初期位置と速度を設定
            Random rand = new Random();
            position = new Point(rand.Next(this.Width - imageWidth), rand.Next(this.Height - imageHeight));
            speed = new Point(rand.Next(2, 4), rand.Next(2, 4));

            // タイマーの設定
            timer1.Interval = 30; // タイマーの間隔を設定(ミリ秒)
            timer1.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            this.Invalidate(); // フォームの再描画を要求
        }

        private void MoveImage()
        {
            // 画像の位置を更新
            position.X += speed.X;
            position.Y += speed.Y;

            // 壁に当たった時の反射
            if (position.X <= 0 || position.X + imageWidth >= this.Width)
            {
                speed.X = -speed.X;
            }
            if (position.Y <= 0 || position.Y + imageHeight >= this.Height)
            {
                speed.Y = -speed.Y;
            }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            MoveImage();
            e.Graphics.DrawImage(image, position.X, position.Y, imageWidth, imageHeight);
        }

        // キーボード入力で画面復帰
        private void MainForm_KeyDown(object sender, KeyEventArgs e)
        {
            this.Close();
        }

        // マウス移動で画面復帰
        private void MainForm_MouseMove(object sender, MouseEventArgs e)
        {
            if (lastMousePosition.IsEmpty)
            {
                lastMousePosition = e.Location;
                return;
            }

            if (Math.Abs(e.X - lastMousePosition.X) > 5 || Math.Abs(e.Y - lastMousePosition.Y) > 5)
            {
                this.Close();
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            var screen = Screen.AllScreens;

            for (int i = 1; i < screen.Length; i++)
            {

                var subForm = new Form2();
                subForm.StartPosition = FormStartPosition.Manual;
                subForm.Location = screen[i].Bounds.Location;
                subForm.Show();
            }

        }
    }
}

Resource1.design_01はsumiso craftのロゴで 背景透過のpng画像です

メソッド名はデフォルトの連番のままです
イベントハンドラの登録および
背景色はデザイナー側で設定しました

ChatGPTが生成したアニメーションのコードに

windowsのスクリーンセーバーを作る #C# - Qiita

の復帰処理を足し
マルチディスプレイ対応を追加しました

dobon.net

メイン以外のモニターにはForm2を表示します

Form2.cs

namespace SumisoScreenSaver
{
    public partial class Form2 : Form
    {
        public Form2()
        {
            InitializeComponent();

            this.DoubleBuffered = true; // フリッカーを防ぐための設定
            this.FormBorderStyle = FormBorderStyle.None; // フォームの枠を消す
            this.WindowState = FormWindowState.Maximized; // フォームを最大化
            this.TopMost = true; // 常に最前面に表示
        }
    }
}

背景色はデザイナーで設定
表示座標を指定されたら、その場で最大化します

インストール

発行したexeファイルの拡張子をscrに書き換えると
スクリーンセーバーとして認識されます

拡張子をscrに書き換える

右クリックのメニューよりインストールを選択
無い時はその他オプションを探してください

スクリーンセーバーとして設定します

スクリーンセーバー設定画面

設定時間画面を操作せずにいると
スクリーンセーバーが起動します

おわりに

懐かしいというか、見なくなったスクリーンセーバー
実は単純なformアプリでした

需要があったら配布するかもしれません

フリーズしたPremiere Proをタスクキルするbat

Premiere Proがフリーズしたときにタスクキルするbatを作成しました

taskkill /F /T /IM "Adobe Premiere Pro.exe"

ご利用は自己責任でお願いします

Premiere Proのフリーズ

筆者環境では、Premiere Proで動画の出力先フォルダを選択するときに
Premiere Proがよくフリーズします
バージョンは24.1

動画の書き出し設定画面

場所 をクリックすると
エクスプローラーが表示されて
保存先を選択できます

しかし、エクスプローラーが表示されずに
フリーズします

画面をクリックすると
操作を受け付けないときの警告音が鳴るばかりで
何もできません

タスクキル

タスクマネージャーを表示
Premiere Proを終了させて
再度起動すると解消します

毎回タスクマネージャーを起動するのは面倒なので
batファイルで自動化します

間違えて他のアプリを終了させるミスも予防できます

タスクキルのコマンド

TASKKILL [/S システム [/U ユーザー名 [/P [パスワード]]]]
         { [/FI フィルター] [/PID プロセスID | /IM イメージ名] } [/T] [/F]
説明:
    このツールは、プロセス ID (PID) またはイメージ名を指定して、タスクを終了するために使われます。

パラメーター一覧:
    /S    システム            接続するリモート システムを指定します。

    /U    [ドメイン\]ユーザー コマンドが実行されるユーザー コンテキストを
                              指定します。

    /P    [パスワード]        提供されるユーザー コンテキストのパスワードを
                              指定します。省略された場合は、入力を要求します。

    /FI   フィルター            タスクを選択するために、フィルターを適用します。
                              "*" を使用できます。例: imagename eq acme*

    /PID   プロセスID         終了させるプロセスの PID を指定します。
                              PID を取得するには、TaskList を使用してください。

    /IM    イメージ名         終了させるプロセスのイメージ名を指定します。
                              すべてのタスクやイメージ名を指定するには、
                              ワイルドカード '*' を使います。

    /T                        指定したプロセスとそのプロセスが開始
                              したすべての子プロセスを削除します。

    /F                        プロセスの強制終了を指定します。

    /?                        このヘルプ メッセージを表示します。

Premiere Proのexeの名前は
Adobe Premiere Pro.exe

taskkill /F /T /IM "Adobe Premiere Pro.exe"

でタスクキルできます

タスクキルすると次にPremiere Proを起動したとき

予期せず終了しました

警告が表示されますが
フリーズしたので仕方ない

まとめ

Premiere Proがフリーズしたときにタスクキルするbatを作成しました

タスクキルはあくまで対処療法でしかありません

Premiere Proのフリーズに詳しい方
アドバイスあれば
コメントまでお願いします

Dockerでマイクラのサーバーを立てる【Java版マイクラ】

Java版マイクラのデータパックの動作検証用に
dockerでマイクラサーバーを構築し
ワールドデータを使い捨てにできる開発環境を作成しました

目次

環境

wsl2にdockerはインストール済みです

構成図

wsl2上のdockerにマイクラサーバーのコンテナを作成し
windowsのマイクラからアクセスします

システム構成図

wsl2でのホスト側最終的なディレクトリ構成図

minecraft/                  適当な作業用フォルダ
 ├ sample/                 マイクラサーバー起動して生成されるファイル格納用
 ├ datapacks/              開発中のデータパックを入れるフォルダ
 │ └ dark_sabanna/        データパック本体
 ├ data/                   コンテナにCOPYするファイル
 │ ├ eula.txt             利用規約に同意
 │ ├ ops.json             op権限設定
 │ ├ server.jar           マイクラサーバー.jar
 │ └ server.properties    ワールドの設定
 ├ docker-compose.yml      ポートやボリュームの設定
 └ Dockerfile              コンテナイメージの設定

Ubuntuでマイクラサーバー構築

Dockerfile用のコマンド確認も含めて
1度マイクラのサーバーを起動して
設定ファイルなどを生成させます

とりあえずUbuntuのイメージからコンテナを起動します
ホスト側minecraftフォルダから実行します

docker container run \
    --name mitest \
    --interactive      \
    --tty              \
    --rm \
    --publish 25565:25565 \
    --mount type=bind,src=$(pwd)/sample,dst=/minecraft \
    --mount type=bind,src=$(pwd)/datapacks,dst=/minecraft/world/datapacks \
    --mount type=bind,src=/etc/passwd,dst=/etc/passwd,readonly \
    --mount type=bind,src=/etc/group,dst=/etc/group,readonly \
    --user 1000:1000 \
    ubuntu:22.04

コンテナ内部で生成したファイルをホスト側で利用したいため
コンテナで使用するユーザーをホスト側のユーザーと合わせています

参考
Dockerコンテナの実行ユーザーと権限の関係
https://qiita.com/yitakura731/items/36a2ba117ccbc8792aa7

普通にJava版マイクラサーバーを構築します
各種インストールのため、rootでアタッチしなおします

# デタッチ
# [control-P] [control-Q]

# rootでアタッチ
docker exec -u 0 -it mitest bash

VScodeのターミナルからデタッチ操作する場合は
ショートカットが他の設定と被ってるので注意
ショートカットの設定を解除すれば使えます

とりあえず更新します

apt update

ファイル一覧確認

ls

マウントで生成されているはずのminecraftフォルダの存在確認します
minecraftに移動します

cd minecraft

Javaのインストールを確認します
入ってません

java --version

Javaのインストールできるバージョン一覧を取得します

apt-cache search openjdk

適当に新しそうなものをインストールします

apt-get install -y openjdk-21-jre-headless

他にweget, screen, vimをインストールしました

apt-get -y install wget
apt-get -y install screen
apt-get -y install vim

root権限が必要な使いそうなアプリのインストールは終わったので
作業用ユーザーに切り替えます

# デタッチ
# [control-P] [control-Q]

# userで入る
docker attach -it mitest

Java版のマイクラサーバーをダウンロードします
下記サイトにjarのダウンロードリンクがあるのでコピーして

https://www.minecraft.net/ja-jp/download/server

ダウンロードします

wget https://piston-data.mojang.com/v1/objects/8dd1a28015f51b1803213892b50b7b4fc76e594d/server.jar

イクラサーバーを起動します

java -Xmx1024M -Xms1024M -jar server.jar nogui

この段階では、利用規約に同意していないので
すぐにサーバーが終了します

ファイルが生成されるので
利用規約に同意します
vimeula.txtを編集します

vi eula.txt

i で入力
falseをtrueに書き換え
escで入力終了
:wqで上書き保存 ミスってやり直したいときは
:q!

vim極めるとファイル編集が爆速になるらしい
vim使いたくない場合は、eula.txt自体を生成する方法もあります

echo "eula=true" > eula.txt

これでマイクラのサーバーが起動できます

java -Xmx1024M -Xms1024M -jar server.jar nogui

ワールドが生成されます
ここで、マイクラの作業用ユーザーにop権限を付けておきます
データパックの動作確認に、各種コマンドが使えるようにしておきます

# op [ユーザー名]
op sumiso_c0db8c

ワールドに入ってみます
イクラを起動し、マルチプレイを選択します

サーバーを追加
アドレスに

localhost

と入力して接続します
ワールドに入れることを確認して終了します

dockerのコンソールに戻るとプレイヤーが出入りしたログが確認できます

イクラのサーバーを停止します

stop

screenでマイクラサーバーを起動する場合は

# サーバー起動
screen -dmS minecraft java -Xmx1024M -Xms1024M -jar server.jar nogui

# 一覧表示
screen -ls
# マイクラサーバーにコマンド送信
screen -S minecraft -X stuff 'say hello'`echo -ne '\015'`
# マイクラサーバー終了
screen -S minecraft -X stuff 'stop'`echo -ne '\015'`

結局screenは利用しませんでした   

イクラサーバーの設定ファイルが生成できたので
コンテナを終了させます

Ubuntuのイメージをベースにしているため
bashを終了するとコンテナも終了します

exit

ホスト側Ubuntuに戻り
sampleフォルダにサーバーの設定ファイルが生成されています

sampleディレクトリから
eula.txt, ops.json, server.jar, server.properties
の4ファイルをdataディレクトリに移動させます

wsl2でのホスト側最終的なディレクトリ構成図

minecraft/                  適当な作業用フォルダ
 ├ sample/                 マイクラサーバー起動して生成されるファイル格納用
 ├ datapacks/              開発中のデータパックを入れるフォルダ
 │ └ dark_sabanna/        データパック本体
 ├ data/                   コンテナにCOPYするファイル
 │ ├ eula.txt             利用規約に同意
 │ ├ ops.json             op権限設定
 │ ├ server.jar           マイクラサーバー.jar
 │ └ server.properties    ワールドの設定
 ├ docker-compose.yml      ポートやボリュームの設定
 └ Dockerfile              コンテナイメージの設定

server.properties

イクラのワールドの設定を調整します

データパックの動作確認を目的としているため
クリエイティブモードになるようにしておきます

server.properties

allow-flight=false
allow-nether=true
broadcast-console-to-ops=true
broadcast-rcon-to-ops=true
# 難易度ハード
difficulty=hard
enable-command-block=false
enable-jmx-monitoring=false
enable-query=false
enable-rcon=false
enable-status=true
enforce-secure-profile=true
enforce-whitelist=false
entity-broadcast-range-percentage=100
force-gamemode=false
function-permission-level=2
# クリエイティブモード
gamemode=creative
generate-structures=true
generator-settings={}
hardcore=false
hide-online-players=false
initial-disabled-packs=
initial-enabled-packs=vanilla
level-name=world
# シード値 空欄でランダム生成
level-seed=141
level-type=minecraft\:normal
log-ips=true
max-chained-neighbor-updates=1000000
# 最大接続人数 マルチしないので1人でいい
max-players=10
max-tick-time=60000
max-world-size=29999984
# ワールドの説明 マルチプレイのワールド選択画面に表示される
motd=Datapacks Test Server
network-compression-threshold=256
online-mode=true
op-permission-level=4
player-idle-timeout=0
prevent-proxy-connections=false
pvp=true
query.port=25565
rate-limit=0
rcon.password=
rcon.port=25575
require-resource-pack=false
resource-pack=
resource-pack-id=
resource-pack-prompt=
resource-pack-sha1=
server-ip=
server-port=25565
simulation-distance=10
spawn-animals=true
spawn-monsters=true
spawn-npcs=true
spawn-protection=16
sync-chunk-writes=true
text-filtering-config=
use-native-transport=true
view-distance=10
white-list=false

変更した部分にコメント入れています

Dockerfile

Ubuntuでマイクラサーバー構築で確認したコマンドなどをもとに
Dockerfileを作成します

javaのイメージに変更しました

FROM openjdk:23-jdk

# server.propertiesなどをコピー
COPY  ./data/ /minecraft/
WORKDIR /minecraft

CMD java -Xmx1024M -Xms1024M -jar server.jar nogui

イクラのサーバー設定などはDockerfileに含めているので
変更したときはimageをビルドしてください

docker image build  \
    --tag midocker:minecraft    \
    --file Dockerfile    \
    .

Dockerfileからマイクラサーバー起動

docker container run        \
    --name mitest           \
    --interactive           \
    --tty                   \
    --rm                    \
    --mount type=bind,src=$(pwd)/datapacks,dst=/minecraft/world/datapacks \
    --publish 25565:25565   \
    midocker:minecraft      \

コンテナ停止

docker container stop mitest

イメージが蓄積してきたら適当に削除しておきます

# 使ってないimageの削除
docker image prune -a

docker-compose

docker-compose.ymlにまとめておきます

datapacksフォルダをマウントするように設定し
ワールド生成時にデータパック適用されるようにしています

version: '3.9'

services:
  minecraft:
    container_name: docker-minecraft
    build: 
      dockerfile: Dockerfile
      context: .
    volumes:
      - type : bind
        source: ./datapacks
        target: /minecraft/world/datapacks
    ports:
      - '25565:25565'

これで下記コマンドでいつでもマイクラのサーバーを構築して
ワールドデータを使い捨てにできます

# 起動
docker compose up --detach
# imageのビルドしなおす場合
docker compose up --build
# 終了
docker compose down

データパック

Dockerでマイクラサーバーが起動できるようになったので
データパックを適用してみます

サバンナバイオームを闇落ちさせるデータパックを作成します

ディレクトリ構成図

minecraft/                  適当な作業用フォルダ
 ├ sample/                 マイクラサーバー起動して生成されるファイル格納用
 ├ datapacks/              開発中のデータパックを入れるフォルダ
 │ └ dark_sabanna/        データパック本体
 ├ data/                   コンテナにCOPYするファイル
 │ ├ eula.txt             利用規約に同意
 │ ├ ops.json             op権限設定
 │ ├ server.jar           マイクラサーバー.jar
 │ └ server.properties    ワールドの設定
 ├ docker-compose.yml      ポートやボリュームの設定
 └ Dockerfile              コンテナイメージの設定

dark_sabannaがデータパック本体です

dark_savanna/
 ├ data/
 │ └ minecraft/
 │   └ worldgen/
 │     └ biome/
 │       └ savanna.json
 └ pack.mcmeta

pack.mcmeta
Java版マイクラ 1.20.4だと26のようです

{
  "pack": {
    "pack_format": 26,
    "description": {
      "text": "サバンナ闇落ちデータパック"
    }
  }
}

参考
https://minecraft.wiki/w/Pack_format

savanna.json
バイオームジェネレーターで適当にサバンナを魔改造します
https://misode.github.io/worldgen/biome/

{
  "temperature": 2,
  "downfall": 0,
  "has_precipitation": false,
  "effects": {
    "sky_color": 15279446,
    "fog_color": 8546241,
    "water_color": 7512092,
    "water_fog_color": 6997592,
    "grass_color": 8593860,
    "foliage_color": 4143022,
    "mood_sound": {
      "sound": "minecraft:ambient.cave",
      "tick_delay": 6000,
      "block_search_extent": 8,
      "offset": 2
    }
  },
  "spawners": {
    "ambient": [
      {
        "type": "minecraft:bat",
        "weight": 10,
        "minCount": 8,
        "maxCount": 8
      }
    ],
    "axolotls": [],
    "creature": [
      {
        "type": "minecraft:sheep",
        "weight": 12,
        "minCount": 4,
        "maxCount": 4
      },
      {
        "type": "minecraft:pig",
        "weight": 10,
        "minCount": 4,
        "maxCount": 4
      },
      {
        "type": "minecraft:chicken",
        "weight": 10,
        "minCount": 4,
        "maxCount": 4
      },
      {
        "type": "minecraft:cow",
        "weight": 8,
        "minCount": 4,
        "maxCount": 4
      },
      {
        "type": "minecraft:horse",
        "weight": 1,
        "minCount": 2,
        "maxCount": 6
      },
      {
        "type": "minecraft:donkey",
        "weight": 1,
        "minCount": 1,
        "maxCount": 1
      }
    ],
    "misc": [],
    "monster": [
      {
        "type": "minecraft:spider",
        "weight": 100,
        "minCount": 4,
        "maxCount": 4
      },
      {
        "type": "minecraft:zombie",
        "weight": 95,
        "minCount": 4,
        "maxCount": 4
      },
      {
        "type": "minecraft:zombie_villager",
        "weight": 5,
        "minCount": 1,
        "maxCount": 1
      },
      {
        "type": "minecraft:skeleton",
        "weight": 100,
        "minCount": 4,
        "maxCount": 4
      },
      {
        "type": "minecraft:creeper",
        "weight": 100,
        "minCount": 4,
        "maxCount": 4
      },
      {
        "type": "minecraft:slime",
        "weight": 100,
        "minCount": 4,
        "maxCount": 4
      },
      {
        "type": "minecraft:enderman",
        "weight": 10,
        "minCount": 1,
        "maxCount": 4
      },
      {
        "type": "minecraft:witch",
        "weight": 5,
        "minCount": 1,
        "maxCount": 1
      }
    ],
    "underground_water_creature": [
      {
        "type": "minecraft:glow_squid",
        "weight": 10,
        "minCount": 4,
        "maxCount": 6
      }
    ],
    "water_ambient": [],
    "water_creature": []
  },
  "spawn_costs": {},
  "carvers": {
    "air": [
      "minecraft:cave",
      "minecraft:cave_extra_underground",
      "minecraft:canyon"
    ]
  },
  "features": [
    [],
    [
      "minecraft:lake_lava_underground",
      "minecraft:lake_lava_surface"
    ],
    [
      "minecraft:amethyst_geode"
    ],
    [
      "minecraft:monster_room",
      "minecraft:monster_room_deep"
    ],
    [],
    [],
    [
      "minecraft:ore_dirt",
      "minecraft:ore_gravel",
      "minecraft:ore_granite_upper",
      "minecraft:ore_granite_lower",
      "minecraft:ore_diorite_upper",
      "minecraft:ore_diorite_lower",
      "minecraft:ore_andesite_upper",
      "minecraft:ore_andesite_lower",
      "minecraft:ore_tuff",
      "minecraft:ore_coal_upper",
      "minecraft:ore_coal_lower",
      "minecraft:ore_iron_upper",
      "minecraft:ore_iron_middle",
      "minecraft:ore_iron_small",
      "minecraft:ore_gold",
      "minecraft:ore_gold_lower",
      "minecraft:ore_redstone",
      "minecraft:ore_redstone_lower",
      "minecraft:ore_diamond",
      "minecraft:ore_diamond_medium",
      "minecraft:ore_diamond_large",
      "minecraft:ore_diamond_buried",
      "minecraft:ore_lapis",
      "minecraft:ore_lapis_buried",
      "minecraft:ore_copper",
      "minecraft:underwater_magma",
      "minecraft:disk_sand",
      "minecraft:disk_clay",
      "minecraft:disk_gravel"
    ],
    [],
    [
      "minecraft:spring_water",
      "minecraft:spring_lava"
    ],
    [
      "minecraft:glow_lichen",
      "minecraft:patch_tall_grass",
      "minecraft:trees_savanna",
      "minecraft:patch_grass_savanna",
      "minecraft:brown_mushroom_normal",
      "minecraft:red_mushroom_normal",
      "minecraft:patch_sugar_cane",
      "minecraft:patch_taiga_grass",
      "minecraft:patch_melon"
    ],
    [
      "minecraft:freeze_top_layer"
    ]
  ]
}

動作確認

イクラサーバーを起動します

# 起動
docker compose up --detach

イクラからワールドに接続します

シード値141を指定しているので
初期リスはサバンナバイオーム

闇落ちサバンナ

無事闇落ちサバンナが生成されています

闇落ちサバンナにはカボチャの代わりにスイカが生成されます

草の生えたスイカ

イカから草が生えてて草

動作を確認したらサーバー停止しワールドごと削除します

# 終了
docker compose down

まとめ

Dockerで使い捨てのJava版マイクラサーバーを起動できるようにしました

イクラのimageはDocker Hubに存在していますが
使わずに構築してみました

ワールドデータを保存するようにすれば、マルチプレイにも使えそうです

参考
実践 Docker - ソフトウェアエンジニアの「Docker よくわからない」を終わりにする本
https://zenn.dev/suzuki_hoge/books/2022-03-docker-practice-8ae36c33424b59