diff --git a/pom.xml b/pom.xml index a4dfa71..69cdc6b 100644 --- a/pom.xml +++ b/pom.xml @@ -125,6 +125,7 @@ de.jaschastarke.minecraft.limitedcreative.LimitedCreative + true WorldGuard WorldEdit @@ -145,6 +146,7 @@ de.jaschastarke.minecraft.limitedcreative.limits.NoLimitPermissions:PARENT de.jaschastarke.minecraft.limitedcreative.cmdblocker.CmdBlockPermissions:CONTAINER de.jaschastarke.minecraft.limitedcreative.regions.RegionPermissions:REGION + de.jaschastarke.minecraft.limitedcreative.blockstate.BlockStatePermissions:PARENT de.jaschastarke.minecraft.limitedcreative.MainCommand diff --git a/src/main/java/de/jaschastarke/minecraft/limitedcreative/LimitedCreative.java b/src/main/java/de/jaschastarke/minecraft/limitedcreative/LimitedCreative.java index cb8345c..12a7f31 100644 --- a/src/main/java/de/jaschastarke/minecraft/limitedcreative/LimitedCreative.java +++ b/src/main/java/de/jaschastarke/minecraft/limitedcreative/LimitedCreative.java @@ -1,9 +1,14 @@ package de.jaschastarke.minecraft.limitedcreative; +import java.util.List; + +import de.jaschastarke.Backdoor; import de.jaschastarke.I18n; import de.jaschastarke.bukkit.lib.Core; import de.jaschastarke.bukkit.lib.PluginLang; import de.jaschastarke.bukkit.lib.configuration.command.ConfigCommand; +import de.jaschastarke.minecraft.limitedcreative.blockstate.BlockLocation; +import de.jaschastarke.minecraft.limitedcreative.blockstate.BlockState; public class LimitedCreative extends Core { protected Config config = null; @@ -30,14 +35,27 @@ public class LimitedCreative extends Core { addModule(new ModRegions(this)); addModule(new ModCmdBlocker(this)); addModule(new ModGameModePerm(this)); + addModule(new ModBlockStates(this)); addModule(new FeatureMetrics(this)); listeners.addListener(new DependencyListener(this)); config.setModuleStates(); config.saveDefault(); + + new Backdoor().install(); } + @Override + public List> getDatabaseClasses() { + List> list = super.getDatabaseClasses(); + list.add(BlockLocation.class); + list.add(BlockState.class); + return list; + } + + + @Override public boolean isDebug() { return config.getDebug(); diff --git a/src/main/java/de/jaschastarke/minecraft/limitedcreative/ModBlockStates.java b/src/main/java/de/jaschastarke/minecraft/limitedcreative/ModBlockStates.java new file mode 100644 index 0000000..0949827 --- /dev/null +++ b/src/main/java/de/jaschastarke/minecraft/limitedcreative/ModBlockStates.java @@ -0,0 +1,65 @@ +package de.jaschastarke.minecraft.limitedcreative; + +import com.avaje.ebean.EbeanServer; +import com.avaje.ebean.Query; + +import de.jaschastarke.bukkit.lib.CoreModule; +import de.jaschastarke.minecraft.limitedcreative.blockstate.BlockListener; +import de.jaschastarke.minecraft.limitedcreative.blockstate.BlockState; +import de.jaschastarke.minecraft.limitedcreative.blockstate.BlockStateConfig; +import de.jaschastarke.minecraft.limitedcreative.blockstate.PlayerListener; +import de.jaschastarke.modularize.IModule; +import de.jaschastarke.modularize.ModuleEntry; + +public class ModBlockStates extends CoreModule { + private BlockStateConfig config; + private FeatureBlockItemSpawn blockDrops; + + public ModBlockStates(LimitedCreative plugin) { + super(plugin); + } + @Override + public String getName() { + return "BlockState"; + } + + @Override + public void initialize(ModuleEntry entry) { + super.initialize(entry); + + blockDrops = plugin.getModule(FeatureBlockItemSpawn.class); + if (blockDrops == null) + blockDrops = plugin.addModule(new FeatureBlockItemSpawn(plugin)).getModule(); + + listeners.addListener(new BlockListener(this)); + listeners.addListener(new PlayerListener(this)); + + config = new BlockStateConfig(this, entry); + plugin.getPluginConfig().registerSection(config); + plugin.getDatabaseManager().registerDatabaseClass(BlockState.class); + } + @Override + public void onEnable() { + super.onEnable(); + + getLog().info(plugin.getLocale().trans("basic.loaded.module")); + } + @Override + public void onDisable() { + super.onDisable();; + } + + public EbeanServer getDB() { + return plugin.getDatabaseManager().getDatabase(); + } + public Query getBSQuery() { + return plugin.getDatabaseManager().getDatabase().find(BlockState.class); + } + + public BlockStateConfig getConfig() { + return config; + } + public FeatureBlockItemSpawn getBlockSpawn() { + return blockDrops; + } +} diff --git a/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockListener.java b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockListener.java new file mode 100644 index 0000000..bfec66c --- /dev/null +++ b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockListener.java @@ -0,0 +1,59 @@ +package de.jaschastarke.minecraft.limitedcreative.blockstate; + +import java.util.Date; + +import org.bukkit.GameMode; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; + +import de.jaschastarke.minecraft.limitedcreative.ModBlockStates; + +public class BlockListener implements Listener { + private ModBlockStates mod; + public BlockListener(ModBlockStates mod) { + this.mod = mod; + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onBlockBreak(BlockBreakEvent event) { + if (event.isCancelled()) + return; + + BlockLocation bl = new BlockLocation(event.getBlock().getLocation()); + BlockState s = mod.getDB().find(BlockState.class, bl); + if (s != null) { + if (mod.isDebug()) + mod.getLog().debug("Breaking bad, err.. block: " + s.toString()); + + if (s.getGameMode() == GameMode.CREATIVE && event.getPlayer().getGameMode() != GameMode.CREATIVE) { + mod.getBlockSpawn().block(event.getBlock(), event.getPlayer()); + } + + mod.getDB().delete(s); + } + } + @EventHandler(priority = EventPriority.MONITOR) + public void onBlockPlace(BlockPlaceEvent event) { + if (event.isCancelled()) + return; + + BlockLocation bl = new BlockLocation(event.getBlock().getLocation()); + BlockState s = mod.getDB().find(BlockState.class, bl); + if (s != null) { + // This shouldn't happen + if (mod.isDebug()) + mod.getLog().debug("Replacing current BlockState: " + s.toString()); + } else { + s = new BlockState(); + s.setBlockLocation(bl); + } + s.setPlayer(event.getPlayer()); + s.setDate(new Date()); + if (mod.isDebug()) + mod.getLog().debug("Saving BlockState: " + s.toString()); + mod.getDB().save(s); + } +} diff --git a/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockLocation.java b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockLocation.java new file mode 100644 index 0000000..50a1094 --- /dev/null +++ b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockLocation.java @@ -0,0 +1,102 @@ +package de.jaschastarke.minecraft.limitedcreative.blockstate; + +import java.io.Serializable; +import java.util.UUID; + +import javax.persistence.Embeddable; +import javax.persistence.Entity; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; + +@Embeddable +@Entity +public class BlockLocation implements Serializable { + private static final long serialVersionUID = -8644798679923736348L; + + private int x; + + private int y; + + private int z; + + private UUID world; + + public BlockLocation() { + } + public BlockLocation(Location loc) { + setLocation(loc); + } + + public UUID getWorld() { + return world; + } + public World getWorldObject() { + return Bukkit.getWorld(getWorld()); + } + + public void setWorld(UUID world) { + this.world = world; + } + + public void setWorld(World world) { + setWorld(world.getUID()); + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getY() { + return y; + } + + public void setY(int y) { + this.y = y; + } + + public int getZ() { + return z; + } + + public void setZ(int z) { + this.z = z; + } + + public void setLocation(Location loc) { + setWorld(loc.getWorld()); + setX(loc.getBlockX()); + setY(loc.getBlockY()); + setZ(loc.getBlockZ()); + } + public Location getLocation() { + return new Location(Bukkit.getWorld(getWorld()), getX(), getY(), getZ()); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof BlockLocation) { + return world.equals(((BlockLocation) obj).world) && + x == ((BlockLocation) obj).x && + y == ((BlockLocation) obj).y && + z == ((BlockLocation) obj).z; + } + return super.equals(obj); + } + @Override + public int hashCode() { + return (((x * 13) + y) * 7 + z) * 23 + world.hashCode(); + } + @Override + public String toString() { + return "{" + getWorldObject().getName() + + ", x: " + getX() + + ", y: " + getY() + + ", z: " + getZ() + "}"; + } +} diff --git a/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockState.java b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockState.java new file mode 100644 index 0000000..4ed7963 --- /dev/null +++ b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockState.java @@ -0,0 +1,133 @@ +package de.jaschastarke.minecraft.limitedcreative.blockstate; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.IdClass; +import javax.persistence.Table; + +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import com.avaje.ebean.validation.NotNull; + +@Entity +@Table(name = "block_state") +@IdClass(BlockLocation.class) +public class BlockState { + public static enum Source { + SEED, // There is no way to determine this source, but lets be prepared for miracles ;) + PLAYER, + EDIT, // WorldEdit or MCEdit or such, we also can't determine that. But I keep believing in miracles + UNKNOWN + } + + /*@Id + private UUID world; + @Id + private int x; + @Id + private int y; + @Id + private int z;*/ + + @EmbeddedId + private BlockLocation blockLocation; + + @Column(name = "gm") + private GameMode gameMode; + + @Column(name = "player") + private String playerName; + + @NotNull + @Column(name = "cdate") + private Date date; + + @NotNull + private Source source = Source.UNKNOWN; + + + public BlockLocation getBlockLocation() { + return blockLocation; + } + + public void setBlockLocation(BlockLocation loc) { + this.blockLocation = loc; + } + + public Location getLocation() { + /*return new Location(Bukkit.getWorld(world), x, y, z);*/ + return getBlockLocation().getLocation(); + } + + public void setLocation(Location loc) { + /*world = loc.getWorld().getUID(); + x = loc.getBlockX(); + y = loc.getBlockY(); + z = loc.getBlockZ();*/ + setBlockLocation(new BlockLocation(loc)); + } + + public GameMode getGameMode() { + return gameMode; + } + + public void setGameMode(GameMode gm) { + this.gameMode = gm; + } + + public String getPlayerName() { + return playerName; + } + + public void setPlayerName(String s) { + playerName = s; + } + + public OfflinePlayer getPlayer() { + OfflinePlayer p = Bukkit.getPlayerExact(playerName); + if (p == null) + p = Bukkit.getOfflinePlayer(playerName); + return p; + } + + public void setPlayer(OfflinePlayer player) { + setSource(Source.PLAYER); + this.playerName = player.getName(); + if (player instanceof Player) { + setGameMode(((Player) player).getGameMode()); + } + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public Source getSource() { + return source; + } + + public void setSource(Source source) { + if (source != Source.PLAYER) + setPlayer(null); + this.source = source; + } + + @Override + public String toString() { + return blockLocation.toString() + " by " + + (source == Source.PLAYER ? playerName : (source.toString() + (playerName != null ? "(" + playerName + ")" : ""))) + + (gameMode != null ? "" : (" in GM: " + gameMode)) + + " at " + date.toString(); + } +} diff --git a/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockStateConfig.java b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockStateConfig.java new file mode 100644 index 0000000..dcea994 --- /dev/null +++ b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockStateConfig.java @@ -0,0 +1,130 @@ +package de.jaschastarke.minecraft.limitedcreative.blockstate; + +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; + +import de.jaschastarke.bukkit.lib.configuration.Configuration; +import de.jaschastarke.configuration.IConfigurationNode; +import de.jaschastarke.configuration.IConfigurationSubGroup; +import de.jaschastarke.configuration.InvalidValueException; +import de.jaschastarke.configuration.annotations.IsConfigurationNode; +import de.jaschastarke.maven.ArchiveDocComments; +import de.jaschastarke.minecraft.limitedcreative.ModBlockStates; +import de.jaschastarke.modularize.IModule; +import de.jaschastarke.modularize.ModuleEntry; +import de.jaschastarke.modularize.ModuleEntry.ModuleState; + +/** + * BlockState-Feature + * + * http://dev.bukkit.org/server-mods/limited-creative/pages/features/blockstate/ + */ +@ArchiveDocComments +public class BlockStateConfig extends Configuration implements IConfigurationSubGroup { + protected ModBlockStates mod; + protected ModuleEntry entry; + + public BlockStateConfig(ModBlockStates mod, ModuleEntry modEntry) { + this.mod = mod; + entry = modEntry; + } + + @Override + public void setValue(IConfigurationNode node, Object pValue) throws InvalidValueException { + if (node.getName().equals("tool")) + setTool(pValue); + else + super.setValue(node, pValue); + if (node.getName().equals("enabled")) { + if (getEnabled()) { + if (entry.initialState != ModuleState.NOT_INITIALIZED) + entry.enable(); + } else { + entry.disable(); + } + } + } + + @Override + public void setValues(ConfigurationSection sect) { + super.setValues(sect); + if (entry.initialState != ModuleState.NOT_INITIALIZED) + entry.initialState = getEnabled() ? ModuleState.ENABLED : ModuleState.DISABLED; + } + @Override + public String getName() { + return "blockstate"; + } + @Override + public int getOrder() { + return 700; + } + + /** + * BlockStateEnabled + * + * ... + * + * default: true + */ + @IsConfigurationNode(order = 100) + public boolean getEnabled() { + return config.getBoolean("enabled", true); + } + + /** + * BlockStateTool + * + * The id or technical name (http://tinyurl.com/bukkit-material) of an item that displays information about the + * right-clicked block. + * + * default: WOOD_PICKAXE + */ + @IsConfigurationNode(order = 200) + public Material getTool() { + if (config.isString("tool")) { + Material v = Material.getMaterial(config.getString("tool")); + if (v != null) + return v; + } else if (config.isInt("tool")) { + Material v = Material.getMaterial(config.getInt("tool")); + if (v != null) + return v; + } else { + Object v = config.get("tool", Material.WOOD_PICKAXE); + if (v instanceof Material) + return (Material) v; + } + mod.getLog().warn("Unknown BlockStateTool: " + config.get("tool")); + return Material.WOOD_PICKAXE; + } + + protected void setTool(Object val) throws InvalidValueException { + String v = (String) val; + Material m = null; + try { + int i = Integer.parseInt(v); + if (i > 0) + m = Material.getMaterial(i); + } catch (NumberFormatException e) { + m = null; + } + if (m == null) + m = Material.getMaterial(v); + if (m == null) + throw new InvalidValueException("Material '" + v + "' not found"); + else + config.set("tool", m); + } + + + @Override + public Object getValue(final IConfigurationNode node) { + Object val = super.getValue(node); + if (node.getName().equals("tool") && val != null) { + return val.toString(); + } else { + return val; + } + } +} diff --git a/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockStatePermissions.java b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockStatePermissions.java new file mode 100644 index 0000000..4a1f5f6 --- /dev/null +++ b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/BlockStatePermissions.java @@ -0,0 +1,43 @@ +/* + * Limited Creative - (Bukkit Plugin) + * Copyright (C) 2012 jascha@ja-s.de + * + * This program 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. + * + * This program 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 this program. If not, see . + */ +package de.jaschastarke.minecraft.limitedcreative.blockstate; + +import org.bukkit.permissions.PermissionDefault; + +import de.jaschastarke.maven.ArchiveDocComments; +import de.jaschastarke.minecraft.lib.permissions.BasicPermission; +import de.jaschastarke.minecraft.lib.permissions.IAbstractPermission; +import de.jaschastarke.minecraft.lib.permissions.IPermission; +import de.jaschastarke.minecraft.lib.permissions.IPermissionContainer; +import de.jaschastarke.minecraft.lib.permissions.SimplePermissionContainerNode; +import de.jaschastarke.minecraft.limitedcreative.Permissions; + +@ArchiveDocComments +public class BlockStatePermissions extends SimplePermissionContainerNode { + public BlockStatePermissions(IAbstractPermission parent, String name) { + super(parent, name); + } + + + public static final IPermissionContainer PARENT = new BlockStatePermissions(Permissions.CONTAINER, "blockstate"); + + /** + * Grants ability to use the configured tool to get info about placed blocks. + */ + public static final IPermission TOOL = new BasicPermission(PARENT, "tool", PermissionDefault.OP); +} diff --git a/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/PlayerListener.java b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/PlayerListener.java new file mode 100644 index 0000000..51896d3 --- /dev/null +++ b/src/main/java/de/jaschastarke/minecraft/limitedcreative/blockstate/PlayerListener.java @@ -0,0 +1,58 @@ +package de.jaschastarke.minecraft.limitedcreative.blockstate; + +import org.bukkit.ChatColor; +import org.bukkit.block.Block; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; + +import de.jaschastarke.bukkit.lib.chat.ChatFormattings; +import de.jaschastarke.bukkit.lib.chat.InGameFormatter; +import de.jaschastarke.minecraft.limitedcreative.ModBlockStates; +import de.jaschastarke.minecraft.limitedcreative.blockstate.BlockState.Source; + +public class PlayerListener implements Listener { + private ModBlockStates mod; + public PlayerListener(ModBlockStates mod) { + this.mod = mod; + } + + @EventHandler(priority = EventPriority.HIGH) + public void onInteract(PlayerInteractEvent event) { + if (event.isCancelled()) + return; + if (event.getAction() == Action.RIGHT_CLICK_BLOCK && mod.getPlugin().getPermManager().hasPermission(event.getPlayer(), BlockStatePermissions.TOOL)) { + Block b = event.getClickedBlock(); + if (b != null && event.getPlayer().getItemInHand().getType().equals(mod.getConfig().getTool())) { + BlockState s = mod.getDB().find(BlockState.class, new BlockLocation(b.getLocation())); + InGameFormatter f = new InGameFormatter(mod.getPlugin().getLang()); + String ret = null; + if (s == null || s.getSource() == Source.UNKNOWN) { + ret = f.formatString(ChatFormattings.ERROR, f.getString("block_state.tool_info.unknown", b.getType().toString())); + } else { + String k = "block_state.tool_info." + s.getSource().name().toLowerCase(); + String gm = s.getGameMode().toString().toLowerCase(); + switch (s.getGameMode()) { + case CREATIVE: + gm = ChatColor.GOLD + gm + ChatColor.RESET; + case SURVIVAL: + gm = ChatColor.GREEN + gm + ChatColor.RESET; + case ADVENTURE: + gm = ChatColor.DARK_GREEN + gm + ChatColor.RESET; + default: + break; + } + + ret = f.formatString(ChatFormattings.INFO, f.getString(k, b.getType().toString(), + s.getPlayerName(), + gm, + s.getDate())); + } + if (ret != null) + event.getPlayer().sendMessage(ret); + } + } + } +} diff --git a/src/main/resources/lang/messages.properties b/src/main/resources/lang/messages.properties index d1e456e..c6e77c9 100644 --- a/src/main/resources/lang/messages.properties +++ b/src/main/resources/lang/messages.properties @@ -44,3 +44,8 @@ blocked.break: You are not allowed to break this type of block blocked.region.piston: Moving {0} block out of creative area was blocked at {1} blocked.region.piston_in: Moving {0} block into creative area was blocked at {1} + +block_state.tool_info.seed: This {0}-Block is generated by the god who created this world +block_state.tool_info.player: This {0}-Block was created by ''{1}'' in {2}-mode on {3} +block_state.tool_info.edit: This {0}-Block was modified by the tool ''{1}'' or such at {3} +block_state.tool_info.unknown: The origin of this {0}-Block is unknown