Skip to content

Commit

Permalink
Add Light command
Browse files Browse the repository at this point in the history
  • Loading branch information
Morphan1 committed Feb 16, 2015
1 parent f817fb9 commit 30c328d
Show file tree
Hide file tree
Showing 3 changed files with 281 additions and 0 deletions.
Expand Up @@ -1487,6 +1487,32 @@ public void registerCoreMembers() {
"LEASH", "leash (cancel) [<entity>|...] (holder:<entity>/<location>)", 1);


// <--[command]
// @Name Light
// @Syntax light [<location>] [<#>/reset]
// @Required 2
// @Stable stable
// @Short Creates a light source at the location.
// @Author Morphan1
// @Group world
// @Description
// This command can create and reset a light source at a specified location, regardless of the type
// of block. It will be shown to all players near the location until it is reset, with certain
// exceptions.
// @Tags
// <l@location.light>
// <l@location.light.blocks>
// @Usage
// Use to create a bright light at a noted location.
// - light l@MyFancyLightOfWool 15
// @Usage
// Use to reset the brightness of the location to its original state.
// - light l@MyFancyLightOfWool reset
// -->
registerCoreMember(LightCommand.class,
"LIGHT", "light [<location>] [<#>/reset]", 2);


// <--[command]
// @Name Listen
// @Syntax listen ({new}/cancel/finish) [kill/block/item/itemdrop/travel] [<requirements>] [script:<name>] (id:<name>)
Expand Down
@@ -0,0 +1,56 @@
package net.aufdemrand.denizen.scripts.commands.world;

import net.aufdemrand.denizen.objects.dLocation;
import net.aufdemrand.denizen.utilities.blocks.BlockLight;
import net.aufdemrand.denizen.utilities.debugging.dB;
import net.aufdemrand.denizencore.exceptions.CommandExecutionException;
import net.aufdemrand.denizencore.exceptions.InvalidArgumentsException;
import net.aufdemrand.denizencore.objects.Element;
import net.aufdemrand.denizencore.objects.aH;
import net.aufdemrand.denizencore.scripts.ScriptEntry;
import net.aufdemrand.denizencore.scripts.commands.AbstractCommand;

public class LightCommand extends AbstractCommand {

@Override
public void parseArgs(ScriptEntry scriptEntry) throws InvalidArgumentsException {

for (aH.Argument arg : aH.interpret(scriptEntry.getArguments())) {

if (!scriptEntry.hasObject("location")
&& arg.matchesArgumentType(dLocation.class))
scriptEntry.addObject("location", arg.asType(dLocation.class));

else if (!scriptEntry.hasObject("light")
&& arg.matchesPrimitive(aH.PrimitiveType.Integer))
scriptEntry.addObject("light", arg.asElement());

else if (!scriptEntry.hasObject("reset")
&& arg.matches("reset"))
scriptEntry.addObject("reset", new Element(true));

}

if (!scriptEntry.hasObject("location") ||
(!scriptEntry.hasObject("light") && !scriptEntry.hasObject("reset"))) {
throw new InvalidArgumentsException("Must specify a valid location and light level.");
}

scriptEntry.defaultObject("reset", new Element(false));
}

@Override
public void execute(ScriptEntry scriptEntry) throws CommandExecutionException {

dLocation location = scriptEntry.getdObject("location");
Element light = scriptEntry.getElement("light");
Element reset = scriptEntry.getElement("reset");

dB.report(scriptEntry, getName(), location.debug() + (light != null ? light.debug() : "") + reset.debug());

if (!reset.asBoolean())
BlockLight.createLight(location, light.asInt());
else
BlockLight.removeLight(location);
}
}
199 changes: 199 additions & 0 deletions src/main/java/net/aufdemrand/denizen/utilities/blocks/BlockLight.java
@@ -0,0 +1,199 @@
package net.aufdemrand.denizen.utilities.blocks;

import net.aufdemrand.denizen.utilities.debugging.dB;
import net.minecraft.server.v1_8_R1.*;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.craftbukkit.v1_8_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_8_R1.CraftWorld;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class BlockLight {

private static final Method playerChunkMethod;
private static final Field dirtyCountField;
private static final Map<Location, BlockLight> lightsByLocation = new HashMap<Location, BlockLight>();
private static final BlockFace[] adjacentFaces = new BlockFace[] {
BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST, BlockFace.UP, BlockFace.DOWN
};

static {
Method pcm = null;
Field dcf = null;
try {
pcm = PlayerChunkMap.class.getDeclaredMethod("a", int.class, int.class, boolean.class);
pcm.setAccessible(true);
dcf = pcm.getReturnType().getDeclaredField("dirtyCount");
dcf.setAccessible(true);
} catch (Exception e) {
dB.echoError(e);
}
playerChunkMethod = pcm;
dirtyCountField = dcf;
for (World worlds : Bukkit.getServer().getWorlds()) {
WorldServer nmsWorld = ((CraftWorld) worlds).getHandle();
IWorldAccess access = getIWorldAccess(worlds);

nmsWorld.addIWorldAccess(access);
}
}

private final CraftWorld craftWorld;
private final WorldServer worldServer;
private final CraftChunk craftChunk;
private final Block block;
private final BlockPosition position;
private final int originalLight;
private int currentLight;

private BlockLight(Location location) {
this.craftWorld = (CraftWorld) location.getWorld();
this.worldServer = craftWorld.getHandle();
this.craftChunk = (CraftChunk) location.getChunk();
this.block = location.getBlock();
this.position = new BlockPosition(block.getX(), block.getY(), block.getZ());
this.originalLight = block.getLightLevel();
this.currentLight = originalLight;
lightsByLocation.put(location, this);
}

public static BlockLight createLight(Location location, int lightLevel) {
location = location.getBlock().getLocation();
BlockLight blockLight;
if (lightsByLocation.containsKey(location))
removeLight(location);
blockLight = new BlockLight(location);
blockLight.update(lightLevel);
return blockLight;
}

public static void removeLight(Location location) {
location = location.getBlock().getLocation();
if (lightsByLocation.containsKey(location)) {
lightsByLocation.get(location).reset();
lightsByLocation.remove(location);
}
}

private void reset() {
this.update(originalLight);
}

private void update(int lightLevel) {
if (this.currentLight == lightLevel) {
return;
}
else if (this.originalLight == lightLevel) {
worldServer.c(EnumSkyBlock.BLOCK, position);
}
else {
worldServer.a(EnumSkyBlock.BLOCK, position, lightLevel);
Block adjacentAir = null;
for (BlockFace face : adjacentFaces) {
if (position.getY() == 0 && face == BlockFace.DOWN)
continue;
if (position.getY() == (craftWorld.getMaxHeight() - 1) && face == BlockFace.UP)
continue;
Block possible = block.getRelative(face);
if (possible.getType() == Material.AIR) {
adjacentAir = possible;
break;
}
}
if (adjacentAir != null) {
worldServer.x(new BlockPosition(adjacentAir.getX(), adjacentAir.getY(), adjacentAir.getZ()));
}
}
int cX = craftChunk.getX();
int cZ = craftChunk.getZ();
for (int x = -1; x <= 1; x++) {
for (int z = -1; z <= 1; z++) {
BlockLight.setDirtyCount(getPlayerChunk(worldServer.getPlayerChunkMap(), cX + x, cZ + z));
}
}
this.currentLight = lightLevel;
}

private static Object getPlayerChunk(PlayerChunkMap map, int x, int z) {
try {
return playerChunkMethod.invoke(map, x, z, false);
} catch (Exception e) {
dB.echoError(e);
}
return null;
}

private static void setDirtyCount(Object playerChunk) {
try {
int dirtyCount = dirtyCountField.getInt(playerChunk);
if (dirtyCount > 0 && dirtyCount < 64) {
dirtyCountField.set(playerChunk, 64);
}
} catch (Exception e) {
dB.echoError(e);
}
}

private static IWorldAccess getIWorldAccess(World world) {
final PlayerChunkMap map = ((CraftWorld) world).getHandle().getPlayerChunkMap();
return new IWorldAccess() {
@Override
public void a(BlockPosition position) {
map.flagDirty(position);
}

@Override
public void b(BlockPosition position) {
map.flagDirty(position);
}

@Override
public void b(int arg0, BlockPosition arg1, int arg2) {
}

@Override
public void a(EntityHuman arg0, int arg1, BlockPosition arg2, int arg3) {
}

@Override
public void a(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
}

@Override
public void a(int arg0, BlockPosition arg1, int arg2) {
}

@Override
public void a(String arg0, double arg1, double arg2, double arg3, float arg4, float arg5) {
}

@Override
public void a(EntityHuman arg0, String arg1, double arg2, double arg3, double arg4, float arg5, float arg6) {
}

@Override
public void a(int arg0, boolean arg1, double arg2, double arg3, double arg4, double arg5, double arg6, double arg7, int... arg8) {
}

@Override
public void a(String arg0, BlockPosition arg1) {
}

@Override
public void a(Entity arg0) {
}

@Override
public void b(Entity arg0) {
}
};
}
}

0 comments on commit 30c328d

Please sign in to comment.