提供: Minecraft Modding Wiki
移動先: 案内検索

ComputerCraft API >

この記事は"Minecraft Forge Universal 10.13.0.x~"及び"ComputerCraft 1.65~"を前提MODとしています。

イベントの発生

右クリックされたとき接続されているComputerにイベントを発生させる周辺機器ブロックを追加します。

  • IComputerAccessインスタンスの管理
  • IComputerAccess.queueEvent()によるイベントの発生

ソースコード

MC1.7 周辺機器の追加」のソースコードを元にして、変更部分のみを解説します。

  • BlockSamplePeripheral.java
package mods.sample.peripheral;

import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;

public class BlockSamplePeripheral extends BlockContainer {

	public BlockSamplePeripheral() {
		super(Material.ground);
	}

	@Override
	public boolean onBlockActivated(World world, int x, int y, int z,
			EntityPlayer entityPlayer, int side, float hitX, float hitY, float hitZ) {
		TileEntity tile = world.getTileEntity(x, y, z);
		if (!world.isRemote && tile != null && tile instanceof TileSamplePeripheral) {
			((TileSamplePeripheral)tile).click();
		}
		return true;
	}

	@Override
	public TileEntity createNewTileEntity(World world, int a) {
		return new TileSamplePeripheral();
	}

}
  • TileSamplePeripheral.java
package mods.sample.peripheral;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import net.minecraft.tileentity.TileEntity;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;

public class TileSamplePeripheral extends TileEntity implements IPeripheral {

	private final Set<IComputerAccess> m_computers;

	public TileSamplePeripheral() {
		m_computers = new HashSet();
	}

	@Override
	public String getType() {
		return "sample";
	}

	@Override
	public String[] getMethodNames() {
		return new String[] {};
	}

	@Override
	public Object[] callMethod(IComputerAccess computer, ILuaContext context,
			int method, Object[] arguments) throws LuaException,
			InterruptedException {
		return null;
	}

	@Override
	public void attach(IComputerAccess computer) {
		synchronized (this) {
			m_computers.add(computer);
		}
	}

	@Override
	public void detach(IComputerAccess computer) {
		synchronized (this) {
			m_computers.remove(computer);
		}
	}

	@Override
	public boolean equals(IPeripheral other) {
		if ((other != null) && (other instanceof TileSamplePeripheral)) {
			return other == this;
		}
		return false;
	}

	public void click() {
		Map<String, Integer> pos = new HashMap<String, Integer>();
		pos.put("x", this.xCoord);
		pos.put("y", this.yCoord);
		pos.put("z", this.zCoord);

		synchronized(this) {
			for (IComputerAccess computer : m_computers) {
				computer.queueEvent("sample_click",
					new Object[] {computer.getAttachmentName(), pos} );
			}
		}
	}

}

解説

BlockSamplePeripheral.java

周辺機器ブロックが右クリックされたときの処理を追加します。

@Override
public boolean onBlockActivated(World world, int x, int y, int z,
		EntityPlayer entityPlayer, int side, float hitX, float hitY, float hitZ) {
	TileEntity tile = world.getTileEntity(x, y, z);
	if (!world.isRemote && tile != null && tile instanceof TileSamplePeripheral) {
		((TileSamplePeripheral)tile).click();
	}
	return true;
}

ブロックが右クリックされたら周辺機器のTileEntityを取得してTileSamplePeripheral.click()を呼び出しています。

TileSamplePeripheral.java

接続されているComputerのリストを管理する処理と、そのComputerにイベントを発生させる処理を追加します。

  • Computerリストの初期化
private final Set<IComputerAccess> m_computers;

public TileSamplePeripheral() {
	m_computers = new HashSet();
}

接続されているComputerのIComputerAccessインスタンスはHashSetのm_computersに格納します。
m_computersをコンストラクタで初期化します。

  • IComputerAccessインスタンスの追加と削除
@Override
public void attach(IComputerAccess computer) {
	synchronized(this) {
		m_computers.add(computer);
	}
}
 
@Override
public void detach(IComputerAccess computer) {
	synchronized(this) {
		m_computers.remove(computer);
	}
}

attach()で接続されたComputerのIComputerAccessインスタンスをm_computersへ追加、detach()で取り外されたComputerのComputerAccessインスタンスをm_computersから削除します。こうすることによってm_computersには常に接続されているComputerだけが登録されているようになっています。

(synchronizedによる排他制御については後述)

  • click()

周辺機器ブロックが右クリックされたときにBlockSamplePeripheralから呼び出されます。

public void click() {
	Map<String, Integer> pos = new HashMap<String, Integer>();
	pos.put("x", this.xCoord);
	pos.put("y", this.yCoord);
	pos.put("z", this.zCoord);

	synchronized(this) {
		for (IComputerAccess computer : m_computers) {
			computer.queueEvent("sample_click",
				new Object[] {computer.getAttachmentName(), pos} );
		}
	}
}

m_computersに登録されているすべてのComputerへIComputerAccess.queueEvent()で"sample_click"イベントを発生させます。イベントのパラメータとして、一番目に周辺機器の接続名(文字列型)、二番目に周辺機器ブロックの座標(テーブル型)を渡しています。

(synchronizedによる排他制御については後述)

イベントの名前とパラメータ

イベント名は、発生したイベントの内容が分かりやすいように"周辺機器の種類_イベントの内容"のような形にすることが推奨されています(APIのjavadoc参照)。

queueEvent()の引数はComputerのos.queueEvent()と同様です。ただし、イベントのパラメータはオブジェクト型の配列として渡します。値の型はcallMethod()の戻り値と同様に適切なLuaの型へと自動的に変換されます。

Minecraftとの競合

callMethod()と同様に、attach()とdetach()もComputerCraftのLuaスレッドから呼び出されているため、attach()やdetach()の処理中に、Minecraftのスレッドからアクセスされる可能性のあるフィールドへアクセスすると競合が発生する可能性があります。従って、そのような処理では適宜synchronizedなどで排他制御する必要があります。

今回のサンプルでは、ComputerCraftのLuaスレッドから呼び出されるattach()/detach()のm_computersへの変更と、Minecraftのスレッドから呼び出されるclick()のm_computersからの取得が競合してしまう可能性があります。そのため、それぞれのm_computersへのアクセスはsynchronizedブロック内で排他的に行っています。