/*
* This file is part of Libelula Minecraft Edition Project.
*
* Libelula Minecraft Edition is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Libelula Minecraft Edition is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Libelula Minecraft Edition.
* If not, see <http://www.gnu.org/licenses/>.
*
*/
package me.libelula.meode;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.TreeSet;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import org.bukkit.Location;
import org.bukkit.Material;
import static org.bukkit.Material.BED_BLOCK;
import static org.bukkit.Material.IRON_DOOR_BLOCK;
import static org.bukkit.Material.SIGN_POST;
import static org.bukkit.Material.WALL_SIGN;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.plugin.Plugin;
/**
* Class RAMStore of the plugin.
*
* @author Diego Lucio D'Onofrio <ddonofrio@member.fsf.org>
* @version 1.0
*/
public class RAMStore extends Store {
private final Plugin plugin;
private TreeSet<BlockEvent> blockEvents;
private TreeSet<Location> ramFilter;
private final Lock _blockEvents_mutex;
private final Lock _ramQuery_mutex;
public RAMStore(Plugin plugin, HDStore hds) {
this.plugin = plugin;
blockEvents = new TreeSet<>();
ramFilter = new TreeSet<>(new LocationComparator());
_blockEvents_mutex = new ReentrantLock(true);
_ramQuery_mutex = new ReentrantLock(true);
}
public int getEventsInMemory() {
return blockEvents.size();
}
public boolean filterContains(Location loc) {
return ramFilter.contains(loc);
}
public void storeEvent(BlockBreakEvent e) {
BlockEvent be = getBlockEventData(e.getBlock());
BlockEvent beAux;
be.placed = false;
be.playerName = e.getPlayer().getName();
Block blockAux;
switch (e.getBlock().getType()) {
case SIGN_POST:
case WALL_SIGN:
Sign sign = (Sign) e.getBlock().getState();
be.aditionalData =
Auxiliary.centerString(sign.getLine(0), 15)
.concat(Auxiliary.centerString(sign.getLine(1), 15))
.concat(Auxiliary.centerString(sign.getLine(2), 15))
.concat(Auxiliary.centerString(sign.getLine(3), 15));
break;
case WOODEN_DOOR:
case IRON_DOOR_BLOCK:
case BED_BLOCK:
if (e.getBlock().getType() == BED_BLOCK) {
blockAux = Auxiliary.getOtherBedBlock(e.getBlock());
} else {
blockAux = Auxiliary.getOtherDoorBlock(e.getBlock());
}
beAux = getBlockEventData(blockAux);
if (beAux.blockTypeID != be.blockTypeID) {
plugin.getLogger().log(Level.WARNING,
"Invalid block @(X={0} Y={1} Z={2}):{3} {4}",
new Object[]{be.location.getBlockX(),
be.location.getBlockY(),
be.location.getBlockZ(),
be.location.getWorld().getName(),
"A half of a "
.concat(e.getBlock().getType().toString())
.concat(" will not be logged.")});
return;
}
beAux.placed = false;
beAux.playerName = e.getPlayer().getName();
storeEvent(beAux);
break;
default:
break;
}
storeEvent(be);
}
public void storeEvent(BlockPlaceEvent e) {
BlockEvent be = getBlockEventData(e.getBlock());
be.placed = true;
be.playerName = e.getPlayer().getName();
switch (e.getBlock().getType()) {
case SIGN_POST:
case WALL_SIGN:
Sign sign = (Sign) e.getBlock().getState();
be.aditionalData =
Auxiliary.centerString(sign.getLine(0), 15)
.concat(Auxiliary.centerString(sign.getLine(1), 15))
.concat(Auxiliary.centerString(sign.getLine(2), 15))
.concat(Auxiliary.centerString(sign.getLine(3), 15));
break;
}
storeEvent(be);
}
private void storeEvent(BlockEvent blockEvent) {
_blockEvents_mutex.lock();
try {
blockEvents.add(blockEvent);
ramFilter.add(blockEvent.location);
} finally {
_blockEvents_mutex.unlock();
}
}
private BlockEvent getBlockEventData(Block block) {
BlockEvent be = new BlockEvent();
be.blockData = block.getData();
be.location = block.getLocation();
be.blockTypeID = block.getTypeId();
be.eventTime = new Date().getTime();
return be;
}
public TreeSet<BlockEvent> getBlockEventsAndRotate() {
TreeSet<BlockEvent> bes = blockEvents;
_blockEvents_mutex.lock();
try {
blockEvents = new TreeSet<>();
ramFilter.clear();
} finally {
_blockEvents_mutex.unlock();
}
return bes;
}
public String query(Location loc, boolean placed) {
String result = null;
BlockEvent be = getLastBlockEvent(loc, placed);
if (be != null) {
result = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(be.eventTime)
.concat(" ").concat(be.playerName)
.concat(be.placed ? " placed " : " removed ")
.concat(Material.getMaterial(be.blockTypeID).toString());
}
return result;
}
private TreeSet<BlockEvent> getExclusiveAccess() {
_blockEvents_mutex.lock();
TreeSet<BlockEvent> bes = blockEvents;
try {
blockEvents = new TreeSet<>();
} finally {
_blockEvents_mutex.unlock();
}
return bes;
}
private void returnEvents(TreeSet<BlockEvent> ramEvents) {
_blockEvents_mutex.lock();
try {
ramEvents.addAll(blockEvents);
blockEvents = ramEvents;
} finally {
_blockEvents_mutex.unlock();
}
}
public BlockEvent getLastBlockEvent(Location loc, boolean placed) {
BlockEvent result = null;
if (!ramFilter.contains(loc)) {
return null;
}
_ramQuery_mutex.lock();
TreeSet<BlockEvent> bes = getExclusiveAccess();
try {
for (Iterator<BlockEvent> it = bes.descendingIterator(); it.hasNext();) {
BlockEvent be = it.next();
if (be.placed == placed && be.location.equals(loc)) {
result = be;
break;
}
}
} finally {
returnEvents(bes);
_ramQuery_mutex.unlock();
}
return result;
}
public TreeSet<BlockEvent> getBlockEvents(Location locMin, Location locMax, String playerName) {
TreeSet<BlockEvent> resultSet = new TreeSet<>();
TreeSet<BlockEvent> bes = new TreeSet<>();
_blockEvents_mutex.lock();
try {
bes.addAll(blockEvents);
} finally {
_blockEvents_mutex.unlock();
}
for (Iterator<BlockEvent> it = bes.descendingIterator(); it.hasNext();) {
BlockEvent be = it.next();
if (be.location.getWorld().equals(locMin.getWorld())
&& be.location.getBlockX() >= locMin.getBlockX() && be.location.getBlockX() <= locMax.getBlockX()
&& be.location.getBlockY() >= locMin.getBlockY() && be.location.getBlockY() <= locMax.getBlockY()
&& be.location.getBlockZ() >= locMin.getBlockZ() && be.location.getBlockZ() <= locMax.getBlockZ()) {
if (playerName != null) {
if (!playerName.equalsIgnoreCase(be.playerName)) {
continue;
}
}
resultSet.add(be);
}
}
bes.clear();
return resultSet;
}
}