提供: Minecraft Modding Wiki
移動先: 案内検索
(attach()/detach()での競合について追記)
(「周辺機器の追加」ページへのリンク修正)
 
(同じ利用者による、間の7版が非表示)
1行目: 1行目:
{{Stb}}
+
[[ComputerCraft_API|ComputerCraft API]] >
 
{{前提MOD|reqmod="Minecraft Forge Universal 10.13.0.x~"及び"ComputerCraft 1.65~"}}
 
{{前提MOD|reqmod="Minecraft Forge Universal 10.13.0.x~"及び"ComputerCraft 1.65~"}}
  
== 周辺機器からイベントを発生させる ==
+
== イベントの発生 ==
クリックされたとき、接続されたComputerやTurtleに対してイベントを発生させる周辺機器ブロックを追加する。
+
右クリックされたとき接続されているComputerにイベントを発生させる周辺機器ブロックを追加します。
 +
 
 +
*IComputerAccessインスタンスの管理
 +
*IComputerAccess.queueEvent()によるイベントの発生
  
 
=== ソースコード ===
 
=== ソースコード ===
「[[ComputerCraft_API/周辺機器の追加|周辺機器の追加]]」のソースコードを元にして、変更部分のみを解説する。
+
「[[ComputerCraft_API/1.65/周辺機器の追加|MC1.7 周辺機器の追加]]」のソースコードを元にして、変更部分のみを解説します。
  
 
*BlockSamplePeripheral.java
 
*BlockSamplePeripheral.java
14行目: 17行目:
 
import net.minecraft.block.BlockContainer;
 
import net.minecraft.block.BlockContainer;
 
import net.minecraft.block.material.Material;
 
import net.minecraft.block.material.Material;
import net.minecraft.creativetab.CreativeTabs;
 
 
import net.minecraft.entity.player.EntityPlayer;
 
import net.minecraft.entity.player.EntityPlayer;
 
import net.minecraft.tileentity.TileEntity;
 
import net.minecraft.tileentity.TileEntity;
22行目: 24行目:
  
 
public BlockSamplePeripheral() {
 
public BlockSamplePeripheral() {
super(Material.rock);
+
super(Material.ground);
setBlockName("blockSamplePeripheral");
 
setCreativeTab(CreativeTabs.tabBlock);
 
 
}
 
}
  
 +
@Override
 
public boolean onBlockActivated(World world, int x, int y, int z,
 
public boolean onBlockActivated(World world, int x, int y, int z,
 
EntityPlayer entityPlayer, int side, float hitX, float hitY, float hitZ) {
 
EntityPlayer entityPlayer, int side, float hitX, float hitY, float hitZ) {
48行目: 49行目:
 
package mods.sample.peripheral;
 
package mods.sample.peripheral;
  
 +
import java.util.HashMap;
 
import java.util.HashSet;
 
import java.util.HashSet;
 +
import java.util.Map;
 
import java.util.Set;
 
import java.util.Set;
  
84行目: 87行目:
 
@Override
 
@Override
 
public void attach(IComputerAccess computer) {
 
public void attach(IComputerAccess computer) {
synchronized (this.m_computers) {
+
synchronized (this) {
this.m_computers.add(computer);
+
m_computers.add(computer);
 
}
 
}
 
}
 
}
91行目: 94行目:
 
@Override
 
@Override
 
public void detach(IComputerAccess computer) {
 
public void detach(IComputerAccess computer) {
synchronized (this.m_computers) {
+
synchronized (this) {
this.m_computers.remove(computer);
+
m_computers.remove(computer);
 
}
 
}
 
}
 
}
105行目: 108行目:
  
 
public void click() {
 
public void click() {
synchronized (this.m_computers) {
+
Map<String, Integer> pos = new HashMap<String, Integer>();
for (IComputerAccess computer : this.m_computers) {
+
pos.put("x", this.xCoord);
computer.queueEvent("sample_click", new Object[] {computer.getAttachmentName()} );
+
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} );
 
}
 
}
 
}
 
}
117行目: 126行目:
 
=== 解説 ===
 
=== 解説 ===
 
==== BlockSamplePeripheral.java ====
 
==== BlockSamplePeripheral.java ====
周辺機器のブロックがクリックされた時の処理を追加する。
+
周辺機器ブロックが右クリックされたときの処理を追加します。
 
<source lang = "java">
 
<source lang = "java">
 +
@Override
 
public boolean onBlockActivated(World world, int x, int y, int z,
 
public boolean onBlockActivated(World world, int x, int y, int z,
 
EntityPlayer entityPlayer, int side, float hitX, float hitY, float hitZ) {
 
EntityPlayer entityPlayer, int side, float hitX, float hitY, float hitZ) {
128行目: 138行目:
 
}
 
}
 
</source>
 
</source>
ブロックがクリックされたらTileEntity(周辺機器)を取得してclick()を呼び出す。
+
ブロックが右クリックされたら周辺機器のTileEntityを取得してTileSamplePeripheral.click()を呼び出しています。
  
 
==== TileSamplePeripheral.java ====
 
==== TileSamplePeripheral.java ====
イベント発生の処理と、イベントを発生させるための接続されたComputerやTurtleのリストの管理を追加する。
+
接続されているComputerのリストを管理する処理と、そのComputerにイベントを発生させる処理を追加します。
 +
 
 +
*Computerリストの初期化
 
<source lang = "java">
 
<source lang = "java">
 
private final Set<IComputerAccess> m_computers;
 
private final Set<IComputerAccess> m_computers;
139行目: 151行目:
 
}
 
}
 
</source>
 
</source>
↑コンストラクタで接続されたComputerやTurtleへイベントを発生させるのに必要なIComputerAccessインスタンスのリストを初期化する。
+
接続されているComputerのIComputerAccessインスタンスはHashSetのm_computersに格納します。<br>
 +
m_computersをコンストラクタで初期化します。
 +
 
 +
*IComputerAccessインスタンスの追加と削除
 
<source lang = "java">
 
<source lang = "java">
 
@Override
 
@Override
 
public void attach(IComputerAccess computer) {
 
public void attach(IComputerAccess computer) {
synchronized(this.m_computers) {
+
synchronized(this) {
this.m_computers.add(computer);
+
m_computers.add(computer);
 
}
 
}
 
}
 
}
150行目: 165行目:
 
@Override
 
@Override
 
public void detach(IComputerAccess computer) {
 
public void detach(IComputerAccess computer) {
synchronized(this.m_computers) {
+
synchronized(this) {
this.m_computers.remove(computer);
+
m_computers.remove(computer);
 
}
 
}
 
}
 
}
 
</source>
 
</source>
↑attach()で接続されたComputerやTurtleのIComputerAccessインスタンスをリストへ追加、detach()で取り外されたComputerやTurtleのComputerAccessインスタンスをリストから削除。attach()とdetach()はComputerCraftのLuaスレッドから呼び出されている。このリストはMinecraftスレッドから呼び出されるclick()からもアクセスされるのでsynchronizedで排他制御している。
+
attach()で接続されたComputerのIComputerAccessインスタンスをm_computersへ追加、detach()で取り外されたComputerのComputerAccessインスタンスをm_computersから削除します。こうすることによってm_computersには常に接続されているComputerだけが登録されているようになっています。<br>
 +
 
 +
(synchronizedによる排他制御については後述)
 +
 
 +
*click()
 +
周辺機器ブロックが右クリックされたときにBlockSamplePeripheralから呼び出されます。
 
<source lang = "java">
 
<source lang = "java">
 
public void click() {
 
public void click() {
synchronized (this.m_computers) {
+
Map<String, Integer> pos = new HashMap<String, Integer>();
for (IComputerAccess computer : this.m_computers) {
+
pos.put("x", this.xCoord);
computer.queueEvent("sample_click", new Object[] {computer.getAttachmentName()} );
+
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} );
 
}
 
}
 
}
 
}
 
}
 
}
 
</source>
 
</source>
↑周辺機器ブロックがクリックされた時に、ブロックのクラスから呼び出される。IComputerAccessインスタンスのリストにある全てのComputerやTurtleへIComputerAccess.queueEvent()で"sample_click"イベントを発生させる。前述の通り、リストへは排他制御でアクセスしている。
+
m_computersに登録されているすべてのComputerへIComputerAccess.queueEvent()で"sample_click"イベントを発生させます。イベントのパラメータとして、一番目に周辺機器の接続名(文字列型)、二番目に周辺機器ブロックの座標(テーブル型)を渡しています。
 +
 
 +
(synchronizedによる排他制御については後述)
 +
 
 +
==== イベントの名前とパラメータ ====
 +
イベント名は、発生したイベントの内容が分かりやすいように"'''周辺機器の種類_イベントの内容'''"のような形にすることが'''推奨'''されています(APIのjavadoc参照)。
 +
 
 +
queueEvent()の引数はComputerのos.queueEvent()と同様です。ただし、イベントのパラメータはオブジェクト型の配列として渡します。値の型は[[ComputerCraft_API/メソッドの追加#引数や戻り値の型|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ブロック内で排他的に行っています。
イベントの内容が分かりやすいように、イベント名は"[周辺機器の種類]_[イベントの内容]"の形にする事が推奨されている。<br>
 
ComputerやTurtleがどの周辺機器から発生したイベントなのか分かるように、IComputerAccess.queueEvent()の第二引数のオブジェクト型配列argumentsの一番目にはIComputerAccess.getAttachmentName()を返すとよい。<br>
 
os.pullEvent関数に対してargumentsは最大で5つの値を返すことができ、値の型はcallMethod()と同様に適切なLuaの型へと変換される。argumentsに空の配列を渡すとos.pullEvent関数はイベント名以外は何も返さない。
 

2015年12月27日 (日) 10:42時点における最新版

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ブロック内で排他的に行っています。