(attach()/detach()での競合について追記) |
(「周辺機器の追加」ページへのリンク修正) |
||
(同じ利用者による、間の7版が非表示) | |||
1行目: | 1行目: | ||
− | + | [[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にイベントを発生させる周辺機器ブロックを追加します。 | |
+ | |||
+ | *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.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. | + | super(Material.ground); |
− | |||
− | |||
} | } | ||
+ | @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 | + | synchronized (this) { |
− | + | m_computers.add(computer); | |
} | } | ||
} | } | ||
91行目: | 94行目: | ||
@Override | @Override | ||
public void detach(IComputerAccess computer) { | public void detach(IComputerAccess computer) { | ||
− | synchronized (this | + | synchronized (this) { |
− | + | m_computers.remove(computer); | |
} | } | ||
} | } | ||
105行目: | 108行目: | ||
public void click() { | public void click() { | ||
− | synchronized (this | + | Map<String, Integer> pos = new HashMap<String, Integer>(); |
− | for (IComputerAccess computer : | + | 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を取得してTileSamplePeripheral.click()を呼び出しています。 | |
==== TileSamplePeripheral.java ==== | ==== TileSamplePeripheral.java ==== | ||
− | + | 接続されている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の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 | + | synchronized(this) { |
− | + | m_computers.add(computer); | |
} | } | ||
} | } | ||
150行目: | 165行目: | ||
@Override | @Override | ||
public void detach(IComputerAccess computer) { | public void detach(IComputerAccess computer) { | ||
− | synchronized(this | + | synchronized(this) { |
− | + | m_computers.remove(computer); | |
} | } | ||
} | } | ||
</source> | </source> | ||
− | + | 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 | + | Map<String, Integer> pos = new HashMap<String, Integer>(); |
− | for (IComputerAccess computer : | + | 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> | ||
− | + | 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ブロック内で排他的に行っています。 | |
− | |||
− | |||
− |
2015年12月27日 (日) 10:42時点における最新版
この記事は"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ブロック内で排他的に行っています。