Lot's of no good ideas in trying to fix the zone-freeze bug.

This commit is contained in:
taoneill 2011-02-28 20:31:52 -05:00
parent aba1e25569
commit a184ff4bf1
13 changed files with 608 additions and 272 deletions

View File

@ -35,6 +35,7 @@ import com.tommytony.war.TeamSpawnStyles;
import com.tommytony.war.WarHub;
import com.tommytony.war.Warzone;
import com.tommytony.war.ZoneLobby;
import com.tommytony.war.jobs.WarJobQueue;
import com.tommytony.war.mappers.VolumeMapper;
import com.tommytony.war.mappers.WarMapper;
import com.tommytony.war.mappers.WarzoneMapper;

View File

@ -180,7 +180,6 @@ public class WarEntityListener extends EntityListener {
if(player instanceof CraftPlayer) {
net.minecraft.server.Entity playerEntity = ((CraftPlayer)player).getHandle();
playerEntity.fireTicks = 0;
// playerEntity.r(); // force refresh (?)
}
event.setCancelled(true);
}
@ -212,7 +211,6 @@ public class WarEntityListener extends EntityListener {
if(player instanceof CraftPlayer) {
net.minecraft.server.Entity playerEntity = ((CraftPlayer)player).getHandle();
playerEntity.fireTicks = 0;
// playerEntity.r(); // force refresh (?)
}
event.setCancelled(true);
}
@ -225,7 +223,7 @@ public class WarEntityListener extends EntityListener {
Warzone zone = war.warzone(location);
if(zone != null && zone.isNoCreatures()) {
event.setCancelled(true);
war.logInfo("Prevented " + event.getMobType().getName() + " from spawning in zone " + zone.getName());
//war.logInfo("Prevented " + event.getMobType().getName() + " from spawning in zone " + zone.getName());
}
}

View File

@ -281,7 +281,7 @@ public class Team {
lines[1] = remainingLives + "/" + warzone.getLifePool() + " lives left";
lines[2] = points + "/" + warzone.getScoreCap() + " pts";
lines[3] = players.size() + "/" + warzone.getTeamCap() + " players";
SignHelper.setToSign(signBlock, (byte)signData, lines);
SignHelper.setToSign(war, signBlock, (byte)signData, lines);
}
}

View File

@ -113,7 +113,7 @@ public class WarHub {
lines[1] = "";
lines[2] = "Pick your battle!";
lines[3] = "";
SignHelper.setToSign(signBlock, (byte)8, lines);
SignHelper.setToSign(war, signBlock, (byte)8, lines);
// Warzone signs
for(Warzone zone : war.getWarzones()) {
@ -142,7 +142,7 @@ public class WarHub {
lines[1] = zone.getName();
lines[2] = zonePlayers + "/" + zoneCap + " players";
lines[3] = zone.getTeams().size() + " teams";
SignHelper.setToSign(block, (byte)8, lines);
SignHelper.setToSign(war, block, (byte)8, lines);
}
public void setVolume(Volume vol) {

View File

@ -117,14 +117,12 @@ public class Warzone {
}
public void setNorthwest(Location northwest) {
//resetNorthwestCursorBlocks();
this.northwest = northwest;
this.volume.setCornerOne(world.getBlockAt(northwest.getBlockX(), northwest.getBlockY(), northwest.getBlockZ()));
addNorthwestCursorBlocks();
}
private void addNorthwestCursorBlocks() {
// int newHighest = this.world.getHighestBlockYAt(this.northwest.getBlockX(), this.northwest.getBlockZ()) - 1;
Block topNWBlock = this.world.getBlockAt(this.northwest.getBlockX(), this.northwest.getBlockY()-1, this.northwest.getBlockZ());
Material[] originalNorthwestBlocks = new Material[3];
originalNorthwestBlocks[0] = topNWBlock.getType(); // save blocks for reset
@ -136,29 +134,17 @@ public class Warzone {
this.war.getServer().getScheduler().scheduleAsyncDelayedTask(this.war, new ResetCursorJob(topNWBlock, originalNorthwestBlocks, false), 85);
}
// private void resetNorthwestCursorBlocks() {
// if(this.northwest != null && originalNorthwestBlocks != null) {
// // reset old corner
// Block oldTopNWBlock = this.world.getBlockAt(this.northwest.getBlockX(), this.northwest.getBlockY() - 1, this.northwest.getBlockZ());
// oldTopNWBlock.setType(originalNorthwestBlocks[0]);
// oldTopNWBlock.getFace(BlockFace.EAST).setType(originalNorthwestBlocks[1]);
// oldTopNWBlock.getFace(BlockFace.SOUTH).setType(originalNorthwestBlocks[2]);
// }
// }
public Location getNorthwest() {
return northwest;
}
public void setSoutheast(Location southeast) {
//resetSoutheastCursorBlocks();
this.southeast = southeast;
this.volume.setCornerTwo(world.getBlockAt(southeast.getBlockX(), southeast.getBlockY(), southeast.getBlockZ()));
addSoutheastCursorBlocks();
}
private void addSoutheastCursorBlocks() {
// int newHighest = this.world.getHighestBlockYAt(this.southeast.getBlockX(), this.southeast.getBlockZ()) - 1;
Block topSEBlock = this.world.getBlockAt(this.southeast.getBlockX(), this.southeast.getBlockY()-1, this.southeast.getBlockZ());
Material[] originalSoutheastBlocks = new Material[3];
originalSoutheastBlocks[0] = topSEBlock.getType(); // save block for reset
@ -170,15 +156,6 @@ public class Warzone {
this.war.getServer().getScheduler().scheduleAsyncDelayedTask(this.war, new ResetCursorJob(topSEBlock, originalSoutheastBlocks, true), 85);
}
// private void resetSoutheastCursorBlocks() {
// if(this.southeast != null && originalSoutheastBlocks != null) {
// // reset old corner
// Block oldTopSEBlock = this.world.getBlockAt(this.southeast.getBlockX(), this.southeast.getBlockY() - 1, this.southeast.getBlockZ());
// oldTopSEBlock.setType(originalSoutheastBlocks[0]);
// oldTopSEBlock.getFace(BlockFace.WEST).setType(originalSoutheastBlocks[1]);
// oldTopSEBlock.getFace(BlockFace.NORTH).setType(originalSoutheastBlocks[2]);
// }
// }
public Location getSoutheast() {
return southeast;
@ -215,8 +192,6 @@ public class Warzone {
}
}
int saved = volume.saveBlocks();
if(clearArtifacts) {
initializeZone(); // bring back stuff
@ -239,7 +214,7 @@ public class Warzone {
respawnPlayer(team, player);
}
team.setRemainingLives(lifePool);
team.setTeamSpawn(team.getTeamSpawn());
team.initializeTeamSpawn();
if(team.getTeamFlag() != null) team.setTeamFlag(team.getTeamFlag());
//team.resetSign();
}
@ -247,28 +222,7 @@ public class Warzone {
initZone();
}
}
// public void initializeZone(PlayerMoveEvent event) {
// if(ready() && volume.isSaved()){
// // everyone back to team spawn with full health
// for(Team team : teams) {
// for(Player p : team.getPlayers()) {
// if(p.getName().equals(event.getPlayer().getName())) {
// respawnPlayer(event, team, event.getPlayer());
// } else {
// respawnPlayer(team, p);
// }
// }
// team.setRemainingLives(lifePool);
// team.setTeamSpawn(team.getTeamSpawn());
// if(team.getTeamFlag() != null) team.setTeamFlag(team.getTeamFlag());
// team.resetSign();
// }
//
// initZone();
// }
// }
private void initZone() {
// add wall outlines
if(isDrawZoneOutline()) {

View File

@ -202,7 +202,7 @@ public class ZoneLobby {
lines[2] = "";
lines[3] = "Pick your team.";
}
SignHelper.setToSign(zoneSignBlock, data, lines);
SignHelper.setToSign(war, zoneSignBlock, data, lines);
// lets get some light in here
if(wall == BlockFace.NORTH || wall == BlockFace.SOUTH) {
@ -585,7 +585,7 @@ public class ZoneLobby {
else data = (byte)8;
}
SignHelper.setToSign(block, data, lines);
SignHelper.setToSign(war, block, data, lines);
}
public boolean isLeavingZone(Location location) {

View File

@ -0,0 +1,143 @@
package com.tommytony.war.jobs;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.Chest;
import org.bukkit.block.Dispenser;
import org.bukkit.block.Sign;
import org.bukkit.inventory.ItemStack;
import com.tommytony.war.volumes.Volume;
public class BlockResetJob implements Runnable {
private final Volume volume;
public BlockResetJob(Volume volume) {
this.volume = volume;
}
public void run() {
int visitedBlocks = 0, noOfResetBlocks = 0, x = 0, y = 0, z = 0;
int currentBlockId = 0;
int oldBlockType = 0;
volume.clearBlocksThatDontFloat();
try {
if(volume.hasTwoCorners() && volume.getBlockTypes() != null) {
x = volume.getMinX();
for(int i = 0; i < volume.getSizeX(); i++){
y = volume.getMinY();
for(int j = 0; j < volume.getSizeY(); j++){
z = volume.getMinZ();
for(int k = 0;k < volume.getSizeZ(); k++) {
try {
oldBlockType = volume.getBlockTypes()[i][j][k];
byte oldBlockData = volume.getBlockDatas()[i][j][k];
Block currentBlock = volume.getWorld().getBlockAt(x, y, z);
currentBlockId = currentBlock.getTypeId();
if(currentBlockId != oldBlockType ||
(currentBlockId == oldBlockType && currentBlock.getData() != oldBlockData ) ||
(currentBlockId == oldBlockType && currentBlock.getData() == oldBlockData &&
(oldBlockType == Material.WALL_SIGN.getId() || oldBlockType == Material.SIGN_POST.getId()
|| oldBlockType == Material.CHEST.getId() || oldBlockType == Material.DISPENSER.getId())
)
) {
if(oldBlockType == Material.WALL_SIGN.getId()
|| oldBlockType == Material.SIGN_POST.getId()) {
// Signs
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
BlockState state = currentBlock.getState();
if(state instanceof Sign) {
Sign sign = (Sign)state;
String[] lines = volume.getSignLines().get("sign-" + i + "-" + j + "-" + k);
if(lines != null && sign.getLines() != null) {
if(lines.length>0)sign.setLine(0, lines[0]);
if(lines.length>1)sign.setLine(1, lines[1]);
if(lines.length>2)sign.setLine(2, lines[2]);
if(lines.length>3)sign.setLine(3, lines[3]);
sign.update(true);
}
}
} else if(oldBlockType == Material.CHEST.getId()) {
// Chests
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
BlockState state = currentBlock.getState();
if(state instanceof Chest) {
Chest chest = (Chest)state;
List<ItemStack> contents = volume.getInvBlockContents().get("chest-" + i + "-" + j + "-" + k);
if(contents != null) {
int ii = 0;
chest.getInventory().clear();
for(ItemStack item : contents) {
chest.getInventory().setItem(ii, item);
ii++;
}
chest.update(true);
}
}
} else if(oldBlockType == Material.DISPENSER.getId()) {
// Dispensers
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
BlockState state = currentBlock.getState();
if(state instanceof Dispenser) {
Dispenser dispenser = (Dispenser)state;
List<ItemStack> contents = volume.getInvBlockContents().get("dispenser-" + i + "-" + j + "-" + k);
if(contents != null) {
int ii = 0;
dispenser.getInventory().clear();
for(ItemStack item : contents) {
dispenser.getInventory().setItem(ii, item);
ii++;
}
dispenser.update(true);
}
}
} else if(oldBlockType == Material.WOODEN_DOOR.getId() || oldBlockType == Material.IRON_DOOR_BLOCK.getId()){
// Door blocks
// Check if is bottom door block
if(j+1 < volume.getSizeY() && volume.getBlockTypes()[i][j+1][k] == oldBlockType) {
// set both door blocks right away
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
Block blockAbove = volume.getWorld().getBlockAt(x, y+1, z);
blockAbove.setType(Material.getMaterial(oldBlockType));
blockAbove.setData(volume.getBlockDatas()[i][j+1][k]);
}
} else {
// regular block
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
}
noOfResetBlocks++;
}
visitedBlocks++;
} catch (Exception e) {
volume.getWar().getLogger().warning("Failed to reset block in volume " + volume.getName() + ". Visited blocks so far:" + visitedBlocks
+ ". Blocks reset: "+ noOfResetBlocks +
". Error at x:" + x + " y:" + y + " z:" + z + ". Exception:" + e.getClass().toString() + " " + e.getMessage());
e.printStackTrace();
} finally {
z++;
}
}
y++;
}
x++;
}
}
} catch (Exception e) {
volume.getWar().logWarn("Failed to reset volume " + volume.getName() + " blocks. Blocks visited: " + visitedBlocks
+ ". Blocks reset: "+ noOfResetBlocks + ". Error at x:" + x + " y:" + y + " z:" + z
+ ". Current block: " + currentBlockId + ". Old block: " + oldBlockType + ". Exception: " + e.getClass().toString() + " " + e.getMessage());
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,48 @@
package com.tommytony.war.jobs;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.bukkit.scheduler.BukkitScheduler;
import bukkit.tommytony.war.War;
public class WarJobQueue extends Thread {
private final War war;
private Queue<Runnable> queue = new ConcurrentLinkedQueue<Runnable>();
public WarJobQueue(War war) {
super("War - Job Queue"); // Initialize thread.
this.war = war;
start();
}
public void run() {
war.logInfo(Thread.currentThread().getName() + " running...");
while(!isInterrupted()) {
if(!queue.isEmpty()) {
Runnable job = queue.poll();
runJob(job);
}
}
war.logInfo("War - Job Queue interrupted.");
}
public synchronized void addJob(Runnable job) {
queue.add(job);
}
private void runJob(Runnable job) {
BukkitScheduler scheduler = war.getServer().getScheduler();
int taskId = scheduler.scheduleAsyncDelayedTask(war, job);
while(scheduler.isQueued(taskId) || scheduler.isCurrentlyRunning(taskId)) {
try {
sleep(20);
} catch (InterruptedException e) {
war.logWarn("Interrupted while waiting for job to complete (" + taskId + ")");
e.printStackTrace();
}
}
}
}

View File

@ -9,11 +9,13 @@ import bukkit.tommytony.war.War;
public class SignHelper {
public static void setToSign(Block block, byte data, String[] lines) {
public static void setToSign(War war, Block block, byte data, String[] lines) {
if(block.getType() != Material.SIGN_POST) {
block.setType(Material.SIGN_POST);
}
block.setData(data);
if(block.getData() != data) {
block.setData(data);
}
BlockState state = block.getState();
if(state instanceof Sign) {
Sign sign = (Sign) state;
@ -26,6 +28,7 @@ public class SignHelper {
sign.update(true);
}
} catch (Exception e) {
// just can't stand this anymore
}
}

View File

@ -0,0 +1,98 @@
package com.tommytony.war.volumes;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.Chest;
import org.bukkit.block.Dispenser;
import org.bukkit.block.Sign;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
public class BlockSaveJob extends Thread {
private final Volume volume;
public BlockSaveJob(Volume volume) {
this.volume = volume;
}
public void run() {
int noOfSavedBlocks = 0;
int x = 0;
int y = 0;
int z = 0;
try {
if(volume.hasTwoCorners()) {
volume.setBlockTypes(new int[volume.getSizeX()][volume.getSizeY()][volume.getSizeZ()]);
volume.setBlockDatas(new byte[volume.getSizeX()][volume.getSizeY()][volume.getSizeZ()]);
volume.getSignLines().clear();
volume.getInvBlockContents().clear();
x = volume.getMinX();
for(int i = 0; i < volume.getSizeX(); i++){
y = volume.getMinY();
for(int j = 0; j < volume.getSizeY(); j++){
z = volume.getMinZ();
for(int k = 0;k < volume.getSizeZ(); k++) {
try {
Block block = volume.getWorld().getBlockAt(x, y, z);
volume.getBlockTypes()[i][j][k] = block.getTypeId();
volume.getBlockDatas()[i][j][k] = block.getData();
BlockState state = block.getState();
if(state instanceof Sign) {
// Signs
Sign sign = (Sign)state;
if(sign.getLines() != null) {
volume.getSignLines().put("sign-" + i + "-" + j + "-" + k, sign.getLines());
}
} else if(state instanceof Chest) {
// Chests
Chest chest = (Chest)state;
Inventory inv = chest.getInventory();
int size = inv.getSize();
List<ItemStack> items = new ArrayList<ItemStack>();
for(int invIndex = 0; invIndex < size; invIndex++){
ItemStack item = inv.getItem(invIndex);
if(item != null && item.getType().getId() != Material.AIR.getId()) {
items.add(item);
}
}
volume.getInvBlockContents().put("chest-" + i + "-" + j + "-" + k, items);
} else if(state instanceof Dispenser) {
// Dispensers
Dispenser dispenser = (Dispenser)state;
Inventory inv = dispenser.getInventory();
int size = inv.getSize();
List<ItemStack> items = new ArrayList<ItemStack>();
for(int invIndex = 0; invIndex < size; invIndex++){
ItemStack item = inv.getItem(invIndex);
if(item != null && item.getType().getId() != Material.AIR.getId()) {
items.add(item);
}
}
volume.getInvBlockContents().put("dispenser-" + i + "-" + j + "-" + k, items);
}
noOfSavedBlocks++;
} catch (Exception e) {
volume.getWar().getLogger().warning("Failed to save block in volume " + getName() + ". Saved blocks so far:" + noOfSavedBlocks
+ ". Error at x:" + x + " y:" + y + " z:" + z + ". Exception:" + e.getClass().toString() + e.getMessage());
e.printStackTrace();
} finally {
z++;
}
}
y++;
}
x++;
}
}
} catch (Exception e) {
volume.getWar().getLogger().warning("Failed to save volume " + getName() + " blocks. Saved blocks:" + noOfSavedBlocks
+ ". Error at x:" + x + " y:" + y + " z:" + z + ". Exception:" + e.getClass().toString() + " "+ e.getMessage());
e.printStackTrace();
}
//return noOfSavedBlocks;
}
}

View File

@ -1,5 +1,6 @@
package com.tommytony.war.volumes;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -17,6 +18,8 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import com.tommytony.war.jobs.BlockResetJob;
import bukkit.tommytony.war.War;
/**
@ -55,6 +58,9 @@ public class Volume {
}
public int saveBlocks() {
// BlockSaveJob job = new BlockSaveJob(this);
// war.getServer().getScheduler().scheduleSyncDelayedTask(war, job);
// return 0;
int noOfSavedBlocks = 0;
int x = 0;
int y = 0;
@ -71,45 +77,53 @@ public class Volume {
for(int j = 0; j < getSizeY(); j++){
z = getMinZ();
for(int k = 0;k < getSizeZ(); k++) {
Block block = getWorld().getBlockAt(x, y, z);
this.getBlockTypes()[i][j][k] = block.getTypeId();
this.getBlockDatas()[i][j][k] = block.getData();
BlockState state = block.getState();
if(state instanceof Sign) {
// Signs
Sign sign = (Sign)state;
if(sign.getLines() != null) {
this.getSignLines().put("sign-" + i + "-" + j + "-" + k, sign.getLines());
}
} else if(state instanceof Chest) {
// Chests
Chest chest = (Chest)state;
Inventory inv = chest.getInventory();
int size = inv.getSize();
List<ItemStack> items = new ArrayList<ItemStack>();
for(int invIndex = 0; invIndex < size; invIndex++){
ItemStack item = inv.getItem(invIndex);
if(item != null && item.getType().getId() != Material.AIR.getId()) {
items.add(item);
try {
Block block = getWorld().getBlockAt(x, y, z);
this.getBlockTypes()[i][j][k] = block.getTypeId();
this.getBlockDatas()[i][j][k] = block.getData();
BlockState state = block.getState();
if(state instanceof Sign) {
// Signs
Sign sign = (Sign)state;
if(sign.getLines() != null) {
this.getSignLines().put("sign-" + i + "-" + j + "-" + k, sign.getLines());
}
}
this.getInvBlockContents().put("chest-" + i + "-" + j + "-" + k, items);
} else if(state instanceof Dispenser) {
// Dispensers
Dispenser dispenser = (Dispenser)state;
Inventory inv = dispenser.getInventory();
int size = inv.getSize();
List<ItemStack> items = new ArrayList<ItemStack>();
for(int invIndex = 0; invIndex < size; invIndex++){
ItemStack item = inv.getItem(invIndex);
if(item != null && item.getType().getId() != Material.AIR.getId()) {
items.add(item);
} else if(state instanceof Chest) {
// Chests
Chest chest = (Chest)state;
Inventory inv = chest.getInventory();
int size = inv.getSize();
List<ItemStack> items = new ArrayList<ItemStack>();
for(int invIndex = 0; invIndex < size; invIndex++){
ItemStack item = inv.getItem(invIndex);
if(item != null && item.getType().getId() != Material.AIR.getId()) {
items.add(item);
}
}
this.getInvBlockContents().put("chest-" + i + "-" + j + "-" + k, items);
} else if(state instanceof Dispenser) {
// Dispensers
Dispenser dispenser = (Dispenser)state;
Inventory inv = dispenser.getInventory();
int size = inv.getSize();
List<ItemStack> items = new ArrayList<ItemStack>();
for(int invIndex = 0; invIndex < size; invIndex++){
ItemStack item = inv.getItem(invIndex);
if(item != null && item.getType().getId() != Material.AIR.getId()) {
items.add(item);
}
}
this.getInvBlockContents().put("dispenser-" + i + "-" + j + "-" + k, items);
}
this.getInvBlockContents().put("dispenser-" + i + "-" + j + "-" + k, items);
noOfSavedBlocks++;
} catch (Exception e) {
this.getWar().getLogger().warning("Failed to save block in volume " + getName() + ". Saved blocks so far:" + noOfSavedBlocks
+ ". Error at x:" + x + " y:" + y + " z:" + z + ". Exception:" + e.getClass().toString() + e.getMessage());
e.printStackTrace();
} finally {
z++;
}
z++;
noOfSavedBlocks++;
}
y++;
}
@ -118,13 +132,16 @@ public class Volume {
}
} catch (Exception e) {
this.getWar().getLogger().warning("Failed to save volume " + getName() + " blocks. Saved blocks:" + noOfSavedBlocks
+ ". Error at x:" + x + " y:" + y + " z:" + z + ". Exception:" + e.getClass().toString() + e.getMessage());
+ ". Error at x:" + x + " y:" + y + " z:" + z + ". Exception:" + e.getClass().toString() + " "+ e.getMessage());
e.printStackTrace();
}
return noOfSavedBlocks;
}
public int resetBlocks() {
// BlockResetJob job = new BlockResetJob(this);
// war.getServer().getScheduler().scheduleSyncDelayedTask(war, job);
// return 0;
int visitedBlocks = 0, noOfResetBlocks = 0, x = 0, y = 0, z = 0;
int currentBlockId = 0;
int oldBlockType = 0;
@ -137,91 +154,99 @@ public class Volume {
for(int j = 0; j < getSizeY(); j++){
z = getMinZ();
for(int k = 0;k < getSizeZ(); k++) {
oldBlockType = getBlockTypes()[i][j][k];
byte oldBlockData = getBlockDatas()[i][j][k];
Block currentBlock = getWorld().getBlockAt(x, y, z);
currentBlockId = currentBlock.getTypeId();
if(currentBlockId != oldBlockType ||
(currentBlockId == oldBlockType && currentBlock.getData() != oldBlockData ) ||
(currentBlockId == oldBlockType && currentBlock.getData() == oldBlockData &&
(oldBlockType == Material.WALL_SIGN.getId() || oldBlockType == Material.SIGN_POST.getId()
|| oldBlockType == Material.CHEST.getId() || oldBlockType == Material.DISPENSER.getId())
)
) {
if(oldBlockType == Material.WALL_SIGN.getId()
|| oldBlockType == Material.SIGN_POST.getId()) {
// Signs
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
BlockState state = currentBlock.getState();
if(state instanceof Sign) {
Sign sign = (Sign)state;
String[] lines = this.getSignLines().get("sign-" + i + "-" + j + "-" + k);
if(lines != null && sign.getLines() != null) {
if(lines.length>0)sign.setLine(0, lines[0]);
if(lines.length>1)sign.setLine(1, lines[1]);
if(lines.length>2)sign.setLine(2, lines[2]);
if(lines.length>3)sign.setLine(3, lines[3]);
sign.update(true);
}
}
} else if(oldBlockType == Material.CHEST.getId()) {
// Chests
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
BlockState state = currentBlock.getState();
if(state instanceof Chest) {
Chest chest = (Chest)state;
List<ItemStack> contents = this.getInvBlockContents().get("chest-" + i + "-" + j + "-" + k);
if(contents != null) {
int ii = 0;
chest.getInventory().clear();
for(ItemStack item : contents) {
chest.getInventory().setItem(ii, item);
ii++;
}
chest.update(true);
}
}
} else if(oldBlockType == Material.DISPENSER.getId()) {
// Dispensers
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
BlockState state = currentBlock.getState();
if(state instanceof Dispenser) {
Dispenser dispenser = (Dispenser)state;
List<ItemStack> contents = this.getInvBlockContents().get("dispenser-" + i + "-" + j + "-" + k);
if(contents != null) {
int ii = 0;
dispenser.getInventory().clear();
for(ItemStack item : contents) {
dispenser.getInventory().setItem(ii, item);
ii++;
}
dispenser.update(true);
}
}
} else if(oldBlockType == Material.WOODEN_DOOR.getId() || oldBlockType == Material.IRON_DOOR_BLOCK.getId()){
// Door blocks
// Check if is bottom door block
if(j+1 < getSizeY() && getBlockTypes()[i][j+1][k] == oldBlockType) {
// set both door blocks right away
try {
oldBlockType = getBlockTypes()[i][j][k];
byte oldBlockData = getBlockDatas()[i][j][k];
Block currentBlock = getWorld().getBlockAt(x, y, z);
currentBlockId = currentBlock.getTypeId();
if(currentBlockId != oldBlockType ||
(currentBlockId == oldBlockType && currentBlock.getData() != oldBlockData ) ||
(currentBlockId == oldBlockType && currentBlock.getData() == oldBlockData &&
(oldBlockType == Material.WALL_SIGN.getId() || oldBlockType == Material.SIGN_POST.getId()
|| oldBlockType == Material.CHEST.getId() || oldBlockType == Material.DISPENSER.getId())
)
) {
if(oldBlockType == Material.WALL_SIGN.getId()
|| oldBlockType == Material.SIGN_POST.getId()) {
// Signs
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
BlockState state = currentBlock.getState();
if(state instanceof Sign) {
Sign sign = (Sign)state;
String[] lines = this.getSignLines().get("sign-" + i + "-" + j + "-" + k);
if(lines != null && sign.getLines() != null) {
if(lines.length>0)sign.setLine(0, lines[0]);
if(lines.length>1)sign.setLine(1, lines[1]);
if(lines.length>2)sign.setLine(2, lines[2]);
if(lines.length>3)sign.setLine(3, lines[3]);
sign.update(true);
}
}
} else if(oldBlockType == Material.CHEST.getId()) {
// Chests
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
BlockState state = currentBlock.getState();
if(state instanceof Chest) {
Chest chest = (Chest)state;
List<ItemStack> contents = this.getInvBlockContents().get("chest-" + i + "-" + j + "-" + k);
if(contents != null) {
int ii = 0;
chest.getInventory().clear();
for(ItemStack item : contents) {
chest.getInventory().setItem(ii, item);
ii++;
}
chest.update(true);
}
}
} else if(oldBlockType == Material.DISPENSER.getId()) {
// Dispensers
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
BlockState state = currentBlock.getState();
if(state instanceof Dispenser) {
Dispenser dispenser = (Dispenser)state;
List<ItemStack> contents = this.getInvBlockContents().get("dispenser-" + i + "-" + j + "-" + k);
if(contents != null) {
int ii = 0;
dispenser.getInventory().clear();
for(ItemStack item : contents) {
dispenser.getInventory().setItem(ii, item);
ii++;
}
dispenser.update(true);
}
}
} else if(oldBlockType == Material.WOODEN_DOOR.getId() || oldBlockType == Material.IRON_DOOR_BLOCK.getId()){
// Door blocks
// Check if is bottom door block
if(j+1 < getSizeY() && getBlockTypes()[i][j+1][k] == oldBlockType) {
// set both door blocks right away
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
Block blockAbove = getWorld().getBlockAt(x, y+1, z);
blockAbove.setType(Material.getMaterial(oldBlockType));
blockAbove.setData(getBlockDatas()[i][j+1][k]);
}
} else {
// regular block
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
Block blockAbove = getWorld().getBlockAt(x, y+1, z);
blockAbove.setType(Material.getMaterial(oldBlockType));
blockAbove.setData(getBlockDatas()[i][j+1][k]);
}
} else {
// regular block
currentBlock.setType(Material.getMaterial(oldBlockType));
currentBlock.setData(oldBlockData);
noOfResetBlocks++;
}
noOfResetBlocks++;
visitedBlocks++;
} catch (Exception e) {
this.getWar().getLogger().warning("Failed to reset block in volume " + getName() + ". Visited blocks so far:" + visitedBlocks
+ ". Blocks reset: "+ noOfResetBlocks +
". Error at x:" + x + " y:" + y + " z:" + z + ". Exception:" + e.getClass().toString() + " " + e.getMessage());
e.printStackTrace();
} finally {
z++;
}
visitedBlocks++;
z++;
}
y++;
}

View File

@ -7,94 +7,122 @@ main: bukkit.tommytony.war.War
commands:
# Player commands
warzones:
description: Lists the warzones on the server. Each warzone is an independent TDM arena.
description: (War) Lists the warzones on the server. Each warzone is an independent TDM arena.
usage: /warzones
zones:
description: Shortcut for /warzones.
description: (War) Shortcut for /warzones.
usage: /zones
warzone:
description: Teleports you to the specified warzone's lobby.
description: (War) Teleports you to the specified warzone's lobby.
usage: /warzone ziggy
zone:
description: Shortcut for /warzone.
description: (War) Shortcut for /warzone.
usage: /zone ziggy
warhub:
description: Teleports you to the warhub, if it exists. The warhub offers portals to reach each warzone on the server.
description: (War) Teleports you to the warhub, if it exists. The warhub offers portals to reach each warzone on the server.
usage: /warhub
teams:
description: Lists the teams in the warzone. Must be standing in zone or in its lobby.
usage: /teams
description: (War) Lists the teams in the warzone.
usage:
- Must be standing in warzone or lobby.
- /teams
join:
description: Use to change teams. Must be in zone. If in lobby, use to join a team as an alternative to walking in the team gate.
usage: /join <diamond/iron/gold/d/i/g>
description: (War) Use to change teams. Also used instead of walking in the team gate in the lobby.
usage:
- Must be standing in warzone or lobby.
- /join <diamond/iron/gold/d/i/g>
leave:
description: Use to leave a battle or a zone. Teleports you back to the zone lobby. Must be in a team already and inside the zone.
usage: /leave
description: (War) Use to leave a warzone. Teleports you back to the lobby.
usage:
- Must be in team already.
- /leave
team:
description: Team chat.
description: (War) Team chat.
usage: /team Leeeroooy!!!
# Warzone maker commands
# Warzone maker commands (must have the 'war.*' permission or be added as a zone-maker in /plugins/War/war.txt
# 1- Battle-related commands
nextbattle:
description: Zone makers only. Zone blocks are restored (from memory). Teams are respawned. Just as if a team's life pool had been exhausted.
usage: /nextbattle
description: (War) Warzone blocks are restored (from memory). Teams are respawned.
usage:
- Must be standing in warzone or lobby
- /nextbattle
# 2- Warzone creation commands
setzone:
description: Zone makers only. Use to create and adjust the warzone outline. A zone area is defined by its Northwest and Southeast corners, up to the sky and down to adminium. When the second corner is set down correctly, the zone blocks are saved.
usage: /setzone <northwest/southeast/nw/se>, e.g. first, /setzone ziggy se, then, /setzone ziggy northwest
savezone:
description: Zone makers only. Use to persist any aesthetic or practical changes made to the zone after the last save. Puts down a default lobby if there are none already. Use optional named parameters to change zone config. Must be in warzone or lobby.
description: (War) Use to create a warzone by its Northwest and Southeast corners. Lobby is created and blocks are saved when the second corner is set.
usage:
- /setzone <northwest/southeast/nw/se>,
- e.g. first, /setzone ziggy se, then, /setzone ziggy nw
- Warzones must be at least 20 blocks wide in both directions.
savezone:
description: (War) Persists changes made to the warzone since the last save. Config can be set with named parameters.
usage:
- Must be standing in warzone or lobby
- /savezone => Basic save
- /savezone lifepool:8 teamsize:5 maxscore:7 autoassign:on outline:off ff:on blockheads:off spawnstyle:<big/flat/small> unbreakable:on nocreatures:on
- /savezone loadout:default => sets the respawn inventory to your current items
- /savezone reward:default => sets the winner's reward to your current items
setzonelobby:
description: Zone makers only. Creates or changes the position of the warzone lobby. Must be in warzone or lobby.
usage: /setzonelobby <north/east/south/west/n/e/s/w>
setzoneloadout:
description: Zone makers only. Changes the zone's respawn loadout to your current inventory. Must be in warzone or lobby.
usage: /setzoneloadout
description: (War) Creates or changes the position of the warzone lobby.
usage:
- Must be standing in warzone or lobby.
- /setzonelobby <north/east/south/west/n/e/s/w>
setteam:
description: Zone makers only. Creates or moves a team spawn. The lobby is updated to reflect any new team. The only available teams are diamond, iron and gold. Must be warzone.
usage: /setteam <diamond/iron/gold/d/i/g>
description: (War) Creates or moves a team spawn. The lobby is updated. Teams are diamond, iron or gold.
usage:
- Must be standing in warzone.
- /setteam <diamond/iron/gold/d/i/g>
setmonument:
description: Zone makers only. Creates or moves a monument. Must be in warzone.
usage: /setmonument <monument-name>
description: (War) Creates or moves a monument.
usage:
- Must be standing in warzone.
- /setmonument <monument-name>
setteamflag:
description: Zone makers only. Creates or moves a team flag post for CTF-style play. The only available teams are diamond, iron and gold. Must be warzone.
usage: /setteamflag <diamond/iron/gold/d/i/g>
description: (War) Creates/moves a team flag post for CTF.
usage:
- Must be standing in warzone.
- /setteamflag <diamond/iron/gold/d/i/g>
resetzone:
description: Zone makers only. Reloads zone blocks from memory by default. Reloads blocks from disk if "hard" option is added. Sends everyone back to the warzone lobby. Must be in zone or lobby.
usage: /resetzone, /resetzone <hard/h>
description: (War) Reloads zone blocks from memory. Reloads from disk with "hard" option. Everyone back to the lobby.
usage:
- Must be standing in warzone or lobby.
- /resetzone, /resetzone <hard/h>
deletezone:
description: Zone makers only. Deletes the zone, resets all blocks. Must be in zone or lobby.
usage: /deletezone
description: (War) Deletes the zone, resets all blocks.
usage:
- Must be standing in warzone or lobby.
- /deletezone
deleteteam:
description: Zone makers only. Deletes the team. Team must exist.
usage: /deleteteam <d/i/g>
description: (War) Deletes the team. Team must exist.
usage:
- Must be standing in warzone or lobby.
- /deleteteam <d/i/g>
deletemonument:
description: Zone makers only. Deletes the monument.
usage: /deletemonument <monument-name>
description: (War) Deletes the monument.
usage:
- Must be standing in warzone or lobby.
- /deletemonument <monument-name>
setzoneconfig:
description: Zone makers only. Use named parameters in any order to change configuration of the zone. Resets blocks like /nextbattle. Does not save zone blocks like /savezone.
usage:
description: (War) Use named parameters to change the configuration of the warzone. Resets blocks like /nextbattle. Does not save zone blocks like /savezone.
usage:
- Must be standing in warzone or lobby.
- /setzoneconfig lifepool:8 teamsize:5 maxscore:7 autoassign:on outline:off ff:on blockheads:off spawnstyle:<big/flat/small> unbreakable:on nocreatures:on
- /setzoneconfig loadout:default => sets the respawn inventory to your current items
- /setzoneconfig reward:default => sets the winner's reward to your current items
zonemaker:
description: Zone makers only. Toggles between player mode and zone maker mode. Or gives/removes access to zonemaker commands for another player.
usage: /zonemaker, /zonemaker <new-or-kicked-maker-name>
description: (War) Toggles between player mode and zone maker mode. Or gives/removes access to zonemaker commands for another player.
usage:
- /zonemaker
- /zonemaker <new-or-kicked-zone-maker-name>
# 3- War hub
setwarhub:
description: Zone makers only. Create or moves a West-facing wall of portals. One portal per warzone. Warzones get a portal back to the warhub.
description: (War) Create or moves a West-facing wall of portals. One portal per warzone. Warzones get a portal back to the warhub.
usage: /setwarhub
deletewarhub:
description: Zone makers only. Deletes the warhub if it exists. Resets all warzone lobbies.
description: (War) Deletes the warhub if it exists. Resets all warzone lobbies.
usage: /deletewarhub
# 4- Defaults
setwarconfig:
description: Zone makers only. Change gobal settings and the default warzone configuration values.
description: (War) Change gobal settings and the default warzone configuration values.
usage:
- /setwarconfig pvpinzonesonly:on buildinzonesonly:on => Global settings
- /setwarconfig lifepool:8 teamsize:5 maxscore:7 autoassign:on outline:off ff:on blockheads:off spawnstyle:<big/flat/small> unbreakable:on nocreatures:on => Warzone defaults
@ -102,10 +130,15 @@ commands:
- /setwarconfig reward:default => sets the winner's reward to your current items
# Fallback
war:
description: Prompts you to use /warhub, /zones and /zone. Can also be used as a prefix for all commands as a fallback if they are taken.
usage: /war, /war setzone ziggy northwest, /war warhub, /war zone ziggy, etc.
description: (War) Short War help. Can also be used as a prefix for all War commands as a fallback if they conflict with other plugins.
usage:
- /war
- /war setzone ziggy northwest
- /war warhub
- /war zone ziggy
- etc.
War:
description: Same as /war. Used as fallback.
usage: See /war.
description: (War) Same as /war. Used as fallback.
usage: See /war.
#Note: When you /disable War with General, all warzone blocks will be reset and artifacts will disappear.
# When you /enable War, all blocks will be loaded from disk and the War-related artifacts will reappear.

View File

@ -7,94 +7,122 @@ main: bukkit.tommytony.war.War
commands:
# Player commands
warzones:
description: Lists the warzones on the server. Each warzone is an independent TDM arena.
description: (War) Lists the warzones on the server. Each warzone is an independent TDM arena.
usage: /warzones
zones:
description: Shortcut for /warzones.
description: (War) Shortcut for /warzones.
usage: /zones
warzone:
description: Teleports you to the specified warzone's lobby.
description: (War) Teleports you to the specified warzone's lobby.
usage: /warzone ziggy
zone:
description: Shortcut for /warzone.
description: (War) Shortcut for /warzone.
usage: /zone ziggy
warhub:
description: Teleports you to the warhub, if it exists. The warhub offers portals to reach each warzone on the server.
description: (War) Teleports you to the warhub, if it exists. The warhub offers portals to reach each warzone on the server.
usage: /warhub
teams:
description: Lists the teams in the warzone. Must be standing in zone or in its lobby.
usage: /teams
description: (War) Lists the teams in the warzone.
usage:
- Must be standing in warzone or lobby.
- /teams
join:
description: Use to change teams. Must be in zone. If in lobby, use to join a team as an alternative to walking in the team gate.
usage: /join <diamond/iron/gold/d/i/g>
description: (War) Use to change teams. Also used instead of walking in the team gate in the lobby.
usage:
- Must be standing in warzone or lobby.
- /join <diamond/iron/gold/d/i/g>
leave:
description: Use to leave a battle or a zone. Teleports you back to the zone lobby. Must be in a team already and inside the zone.
usage: /leave
description: (War) Use to leave a warzone. Teleports you back to the lobby.
usage:
- Must be in team already.
- /leave
team:
description: Team chat.
description: (War) Team chat.
usage: /team Leeeroooy!!!
# Warzone maker commands
# Warzone maker commands (must have the 'war.*' permission or be added as a zone-maker in /plugins/War/war.txt
# 1- Battle-related commands
nextbattle:
description: Zone makers only. Zone blocks are restored (from memory). Teams are respawned. Just as if a team's life pool had been exhausted.
usage: /nextbattle
description: (War) Warzone blocks are restored (from memory). Teams are respawned.
usage:
- Must be standing in warzone or lobby
- /nextbattle
# 2- Warzone creation commands
setzone:
description: Zone makers only. Use to create and adjust the warzone outline. A zone area is defined by its Northwest and Southeast corners, up to the sky and down to adminium. When the second corner is set down correctly, the zone blocks are saved.
usage: /setzone <northwest/southeast/nw/se>, e.g. first, /setzone ziggy se, then, /setzone ziggy northwest
savezone:
description: Zone makers only. Use to persist any aesthetic or practical changes made to the zone after the last save. Puts down a default lobby if there are none already. Use optional named parameters to change zone config. Must be in warzone or lobby.
description: (War) Use to create a warzone by its Northwest and Southeast corners. Lobby is created and blocks are saved when the second corner is set.
usage:
- /setzone <northwest/southeast/nw/se>,
- e.g. first, /setzone ziggy se, then, /setzone ziggy nw
- Warzones must be at least 20 blocks wide in both directions.
savezone:
description: (War) Persists changes made to the warzone since the last save. Config can be set with named parameters.
usage:
- Must be standing in warzone or lobby
- /savezone => Basic save
- /savezone lifepool:8 teamsize:5 maxscore:7 autoassign:on outline:off ff:on blockheads:off spawnstyle:<big/flat/small> unbreakable:on nocreatures:on
- /savezone loadout:default => sets the respawn inventory to your current items
- /savezone reward:default => sets the winner's reward to your current items
setzonelobby:
description: Zone makers only. Creates or changes the position of the warzone lobby. Must be in warzone or lobby.
usage: /setzonelobby <north/east/south/west/n/e/s/w>
setzoneloadout:
description: Zone makers only. Changes the zone's respawn loadout to your current inventory. Must be in warzone or lobby.
usage: /setzoneloadout
description: (War) Creates or changes the position of the warzone lobby.
usage:
- Must be standing in warzone or lobby.
- /setzonelobby <north/east/south/west/n/e/s/w>
setteam:
description: Zone makers only. Creates or moves a team spawn. The lobby is updated to reflect any new team. The only available teams are diamond, iron and gold. Must be warzone.
usage: /setteam <diamond/iron/gold/d/i/g>
description: (War) Creates or moves a team spawn. The lobby is updated. Teams are diamond, iron or gold.
usage:
- Must be standing in warzone.
- /setteam <diamond/iron/gold/d/i/g>
setmonument:
description: Zone makers only. Creates or moves a monument. Must be in warzone.
usage: /setmonument <monument-name>
description: (War) Creates or moves a monument.
usage:
- Must be standing in warzone.
- /setmonument <monument-name>
setteamflag:
description: Zone makers only. Creates or moves a team flag post for CTF-style play. The only available teams are diamond, iron and gold. Must be warzone.
usage: /setteamflag <diamond/iron/gold/d/i/g>
description: (War) Creates/moves a team flag post for CTF.
usage:
- Must be standing in warzone.
- /setteamflag <diamond/iron/gold/d/i/g>
resetzone:
description: Zone makers only. Reloads zone blocks from memory by default. Reloads blocks from disk if "hard" option is added. Sends everyone back to the warzone lobby. Must be in zone or lobby.
usage: /resetzone, /resetzone <hard/h>
description: (War) Reloads zone blocks from memory. Reloads from disk with "hard" option. Everyone back to the lobby.
usage:
- Must be standing in warzone or lobby.
- /resetzone, /resetzone <hard/h>
deletezone:
description: Zone makers only. Deletes the zone, resets all blocks. Must be in zone or lobby.
usage: /deletezone
description: (War) Deletes the zone, resets all blocks.
usage:
- Must be standing in warzone or lobby.
- /deletezone
deleteteam:
description: Zone makers only. Deletes the team. Team must exist.
usage: /deleteteam <d/i/g>
description: (War) Deletes the team. Team must exist.
usage:
- Must be standing in warzone or lobby.
- /deleteteam <d/i/g>
deletemonument:
description: Zone makers only. Deletes the monument.
usage: /deletemonument <monument-name>
description: (War) Deletes the monument.
usage:
- Must be standing in warzone or lobby.
- /deletemonument <monument-name>
setzoneconfig:
description: Zone makers only. Use named parameters in any order to change configuration of the zone. Resets blocks like /nextbattle. Does not save zone blocks like /savezone.
usage:
description: (War) Use named parameters to change the configuration of the warzone. Resets blocks like /nextbattle. Does not save zone blocks like /savezone.
usage:
- Must be standing in warzone or lobby.
- /setzoneconfig lifepool:8 teamsize:5 maxscore:7 autoassign:on outline:off ff:on blockheads:off spawnstyle:<big/flat/small> unbreakable:on nocreatures:on
- /setzoneconfig loadout:default => sets the respawn inventory to your current items
- /setzoneconfig reward:default => sets the winner's reward to your current items
zonemaker:
description: Zone makers only. Toggles between player mode and zone maker mode. Or gives/removes access to zonemaker commands for another player.
usage: /zonemaker, /zonemaker <new-or-kicked-maker-name>
description: (War) Toggles between player mode and zone maker mode. Or gives/removes access to zonemaker commands for another player.
usage:
- /zonemaker
- /zonemaker <new-or-kicked-zone-maker-name>
# 3- War hub
setwarhub:
description: Zone makers only. Create or moves a West-facing wall of portals. One portal per warzone. Warzones get a portal back to the warhub.
description: (War) Create or moves a West-facing wall of portals. One portal per warzone. Warzones get a portal back to the warhub.
usage: /setwarhub
deletewarhub:
description: Zone makers only. Deletes the warhub if it exists. Resets all warzone lobbies.
description: (War) Deletes the warhub if it exists. Resets all warzone lobbies.
usage: /deletewarhub
# 4- Defaults
setwarconfig:
description: Zone makers only. Change gobal settings and the default warzone configuration values.
description: (War) Change gobal settings and the default warzone configuration values.
usage:
- /setwarconfig pvpinzonesonly:on buildinzonesonly:on => Global settings
- /setwarconfig lifepool:8 teamsize:5 maxscore:7 autoassign:on outline:off ff:on blockheads:off spawnstyle:<big/flat/small> unbreakable:on nocreatures:on => Warzone defaults
@ -102,10 +130,15 @@ commands:
- /setwarconfig reward:default => sets the winner's reward to your current items
# Fallback
war:
description: Prompts you to use /warhub, /zones and /zone. Can also be used as a prefix for all commands as a fallback if they are taken.
usage: /war, /war setzone ziggy northwest, /war warhub, /war zone ziggy, etc.
description: (War) Short War help. Can also be used as a prefix for all War commands as a fallback if they conflict with other plugins.
usage:
- /war
- /war setzone ziggy northwest
- /war warhub
- /war zone ziggy
- etc.
War:
description: Same as /war. Used as fallback.
usage: See /war.
description: (War) Same as /war. Used as fallback.
usage: See /war.
#Note: When you /disable War with General, all warzone blocks will be reset and artifacts will disappear.
# When you /enable War, all blocks will be loaded from disk and the War-related artifacts will reappear.