package com.laytonsmith.core.functions;
import com.laytonsmith.PureUtilities.Common.StringUtils;
import com.laytonsmith.PureUtilities.Version;
import com.laytonsmith.abstraction.MCAnimalTamer;
import com.laytonsmith.abstraction.MCColor;
import com.laytonsmith.abstraction.MCCreatureSpawner;
import com.laytonsmith.abstraction.MCEntity;
import com.laytonsmith.abstraction.MCFireworkEffect;
import com.laytonsmith.abstraction.MCItem;
import com.laytonsmith.abstraction.MCItemStack;
import com.laytonsmith.abstraction.MCLivingEntity;
import com.laytonsmith.abstraction.MCLocation;
import com.laytonsmith.abstraction.MCOfflinePlayer;
import com.laytonsmith.abstraction.MCPlayer;
import com.laytonsmith.abstraction.MCPlugin;
import com.laytonsmith.abstraction.MCPluginManager;
import com.laytonsmith.abstraction.MCServer;
import com.laytonsmith.abstraction.MCTameable;
import com.laytonsmith.abstraction.MCWorld;
import com.laytonsmith.abstraction.StaticLayer;
import com.laytonsmith.abstraction.blocks.MCMaterial;
import com.laytonsmith.abstraction.entities.MCFirework;
import com.laytonsmith.abstraction.entities.MCHorse;
import com.laytonsmith.abstraction.enums.MCCreeperType;
import com.laytonsmith.abstraction.enums.MCDyeColor;
import com.laytonsmith.abstraction.enums.MCEffect;
import com.laytonsmith.abstraction.enums.MCEntityType;
import com.laytonsmith.abstraction.enums.MCFireworkType;
import com.laytonsmith.abstraction.enums.MCMobs;
import com.laytonsmith.abstraction.enums.MCOcelotType;
import com.laytonsmith.abstraction.enums.MCPigType;
import com.laytonsmith.abstraction.enums.MCProfession;
import com.laytonsmith.abstraction.enums.MCSkeletonType;
import com.laytonsmith.abstraction.enums.MCWolfType;
import com.laytonsmith.abstraction.enums.MCZombieType;
import com.laytonsmith.annotations.api;
import com.laytonsmith.core.CHVersion;
import com.laytonsmith.core.ObjectGenerator;
import com.laytonsmith.core.Optimizable;
import com.laytonsmith.core.Static;
import com.laytonsmith.core.constructs.CArray;
import com.laytonsmith.core.constructs.CBoolean;
import com.laytonsmith.core.constructs.CDouble;
import com.laytonsmith.core.constructs.CInt;
import com.laytonsmith.core.constructs.CNull;
import com.laytonsmith.core.constructs.CString;
import com.laytonsmith.core.constructs.CVoid;
import com.laytonsmith.core.constructs.Construct;
import com.laytonsmith.core.constructs.Target;
import com.laytonsmith.core.environments.CommandHelperEnvironment;
import com.laytonsmith.core.environments.Environment;
import com.laytonsmith.core.events.drivers.ServerEvents;
import com.laytonsmith.core.exceptions.CRE.CREBadEntityException;
import com.laytonsmith.core.exceptions.CRE.CRECastException;
import com.laytonsmith.core.exceptions.CRE.CREFormatException;
import com.laytonsmith.core.exceptions.CRE.CREInvalidWorldException;
import com.laytonsmith.core.exceptions.CRE.CRELengthException;
import com.laytonsmith.core.exceptions.CRE.CRENotFoundException;
import com.laytonsmith.core.exceptions.CRE.CREPlayerOfflineException;
import com.laytonsmith.core.exceptions.CRE.CRERangeException;
import com.laytonsmith.core.exceptions.CRE.CREThrowable;
import com.laytonsmith.core.exceptions.CRE.CREUntameableMobException;
import com.laytonsmith.core.exceptions.CancelCommandException;
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
*/
public class Minecraft {
public static String docs() {
return "These functions provide a hook into game functionality.";
}
private static final SortedMap<String, Construct> DataValueLookup = new TreeMap<String, Construct>();
private static final SortedMap<String, Construct> DataNameLookup = new TreeMap<String, Construct>();
static {
Properties p1 = new Properties();
try {
p1.load(Minecraft.class.getResourceAsStream("/data_values.txt"));
Enumeration e = p1.propertyNames();
while (e.hasMoreElements()) {
String name = e.nextElement().toString();
DataValueLookup.put(name, new CString(p1.getProperty(name).toString(), Target.UNKNOWN));
}
} catch (IOException ex) {
Logger.getLogger(Minecraft.class.getName()).log(Level.SEVERE, null, ex);
}
Properties p2 = new Properties();
try {
p2.load(Minecraft.class.getResourceAsStream("/data_names.txt"));
Enumeration e = p2.propertyNames();
while (e.hasMoreElements()) {
String name = e.nextElement().toString();
DataNameLookup.put(name, new CString(p2.getProperty(name).toString(), Target.UNKNOWN));
}
} catch (IOException ex) {
Logger.getLogger(Minecraft.class.getName()).log(Level.SEVERE, null, ex);
}
}
@api
public static class data_values extends AbstractFunction {
@Override
public String getName() {
return "data_values";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
if (args[0] instanceof CInt) {
return new CInt(Static.getInt(args[0], t), t);
} else {
String c = args[0].val();
int number = StaticLayer.LookupItemId(c);
if (number != -1) {
return new CInt(number, t);
}
String changed = c;
if (changed.contains(":")) {
//Split on that, and reverse. Change wool:red to redwool
String split[] = changed.split(":");
if (split.length == 2) {
changed = split[1] + split[0];
}
}
//Remove anything that isn't a letter or a number
changed = changed.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();
//Do a lookup in the DataLookup table
if (DataValueLookup.containsKey(changed)) {
String split[] = DataValueLookup.get(changed).toString().split(":");
if (split[1].equals("0")) {
return new CInt(split[0], t);
}
return new CString(split[0] + ":" + split[1], t);
}
return CNull.NULL;
}
}
@Override
public String docs() {
return "int {var1} Does a lookup to return the data value of a name. For instance, returns 1 for 'stone'. If an integer is given,"
+ " simply returns that number. If the data value cannot be found, null is returned.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_0_1;
}
@Override
public Boolean runAsync() {
return false;
}
}
@api
public static class data_name extends AbstractFunction {
@Override
public String getName() {
return "data_name";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "string {int | itemArray} Performs the reverse functionality as data_values. Given 1, returns 'Stone'. Note that the enum value"
+ " given in bukkit's Material class is what is returned as a fallback, if the id doesn't match a value in the internally maintained list."
+ " If a completely invalid argument is passed"
+ " in, null is returned.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class, CREFormatException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
int i = -1;
int i2 = -1;
if (args[0] instanceof CString) {
//We also accept item notation
if (args[0].val().contains(":")) {
String[] split = args[0].val().split(":");
try {
i = Integer.parseInt(split[0]);
i2 = Integer.parseInt(split[1]);
} catch (NumberFormatException e) {
} catch (ArrayIndexOutOfBoundsException e){
throw new CREFormatException("Incorrect format for the item notation: " + args[0].val(), t);
}
}
} else if (args[0] instanceof CArray) {
MCItemStack is = ObjectGenerator.GetGenerator().item(args[0], t);
i = is.getTypeId();
i2 = (int) is.getData().getData();
}
if (i == -1) {
i = Static.getInt32(args[0], t);
}
if (i2 == -1) {
i2 = 0;
}
if (DataNameLookup.containsKey(i + "_" + i2)) {
return DataNameLookup.get(i + "_" + i2);
} else if (DataNameLookup.containsKey(i + "_0")) {
return DataNameLookup.get(i + "_0");
} else {
try {
return new CString(StaticLayer.LookupMaterialName(i), t);
} catch (NullPointerException e) {
return CNull.NULL;
}
}
}
}
@api
public static class max_stack_size extends AbstractFunction {
@Override
public String getName() {
return "max_stack_size";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "integer {itemType | itemArray} Given an item type, returns"
+ " the maximum allowed stack size. This method will accept either"
+ " a single data value (i.e. 278) or an item array like is returned"
+ " from pinv(). Additionally, if a single value, it can also be in"
+ " the old item notation (i.e. '35:11'), though for the purposes of this"
+ " function, the data is unneccesary.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class, CREFormatException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
if (args[0] instanceof CArray) {
MCItemStack is = ObjectGenerator.GetGenerator().item(args[0], t);
return new CInt(is.getType().getMaxStackSize(), t);
} else {
String item = args[0].val();
if (item.contains(":")) {
String[] split = item.split(":");
item = split[0];
}
try {
int iitem = Integer.parseInt(item);
int max = StaticLayer.GetItemStack(iitem, 1).getType().getMaxStackSize();
return new CInt(max, t);
} catch (NumberFormatException e) {
}
}
throw new CRECastException("Improper value passed to max_stack. Expecting a number, or an item array, but received \"" + args[0].val() + "\"", t);
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
}
@api(environments = {CommandHelperEnvironment.class})
public static class spawn_mob extends AbstractFunction {
// The max amount of mobs that can be spawned at once by this function.
private static final int SPAWN_LIMIT = 10000;
@Override
public String getName() {
return "spawn_mob";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1, 2, 3};
}
@Override
public String docs() {
return "array {mobType, [qty], [location]} Spawns qty mob of one of the following types at location. qty defaults to 1, and location defaults"
+ " to the location of the player. An array of the entity IDs spawned is returned."
+ " ---- mobType can be one of: " + StringUtils.Join(MCMobs.values(), ", ", ", or ", " or ") + "."
+ " Spelling matters, but capitalization doesn't. At this time, the function is limited to spawning a maximum of 50 at a time."
+ " Further, subtypes can be applied by specifying MOBTYPE:SUBTYPE, for example the sheep subtype can be any of the dye colors: "
+ StringUtils.Join(MCDyeColor.values(), ", ", ", or ", " or ") + ". COLOR defaults to white if not specified. For mobs with multiple"
+ " subtypes, separate each type with a \"-\", currently only zombies which, using ZOMBIE:TYPE1-TYPE2 can be any non-conflicting two of: "
+ StringUtils.Join(MCZombieType.values(), ", ", ", or ", " or ") + ", but default to normal zombies. Ocelots may be one of: "
+ StringUtils.Join(MCOcelotType.values(), ", ", ", or ", " or ") + ", defaulting to the wild variety. Villagers can have a profession as a subtype: "
+ StringUtils.Join(MCProfession.values(), ", ", ", or ", " or ") + ", defaulting to farmer if not specified. Skeletons can be "
+ StringUtils.Join(MCSkeletonType.values(), ", ", ", or ", " or ") + ". PigZombies' subtype represents their anger,"
+ " and accepts an integer, where 0 is neutral and 400 is the normal response to being attacked. Defaults to 0. Similarly, Slime"
+ " and MagmaCube size can be set by integer, otherwise will be a random natural size. If a material is specified as the subtype"
+ " for Endermen, they will hold that material, otherwise they will hold nothing. Creepers can be set to "
+ StringUtils.Join(MCCreeperType.values(), ", ", ", or ", " or ") + ", wolves can be " + StringUtils.Join(MCWolfType.values(), ", ", ", or ", " or ")
+ ", and pigs can be " + StringUtils.Join(MCPigType.values(), ", ", ", or ", " or ") + "."
+ " Horses can have three different subTypes, the variant: " + StringUtils.Join(MCHorse.MCHorseVariant.values(), ", ", ", or ", " or ") + ","
+ " the color: " + StringUtils.Join(MCHorse.MCHorseColor.values(), ", ", ", or ", " or ") + ","
+ " and the pattern: " + StringUtils.Join(MCHorse.MCHorsePattern.values(), ", ", ", or ", " or ") + "."
+ " If qty is larger than " + spawn_mob.SPAWN_LIMIT + ", a RangeException will be thrown.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class, CRERangeException.class,
CREFormatException.class, CREPlayerOfflineException.class,
CREInvalidWorldException.class, CRENotFoundException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_1_2;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
String mob = args[0].val();
String secondary = "";
if (mob.contains(":")) {
secondary = mob.substring(mob.indexOf(':') + 1);
mob = mob.substring(0, mob.indexOf(':'));
}
int qty = 1;
if (args.length > 1) {
qty = Static.getInt32(args[1], t);
if (qty > spawn_mob.SPAWN_LIMIT) {
throw new CRERangeException("You can not spawn more than " + spawn_mob.SPAWN_LIMIT
+ " mobs at once using the " + this.getName() + " function.", t);
}
}
MCLocation l;
MCPlayer p = env.getEnv(CommandHelperEnvironment.class).GetPlayer();
if (args.length == 3) {
l = ObjectGenerator.GetGenerator().location(args[2], (p != null ? p.getWorld() : null), t);
} else if (p != null) {
l = p.getLocation();
} else {
throw new CREPlayerOfflineException("Invalid sender!", t);
}
if (l == null) { // Happends when executed by a fake player.
throw new CRENotFoundException(
"Could not find the location of the player (are you running in cmdline mode?)", t);
}
try{
return l.getWorld().spawnMob(MCMobs.valueOf(mob.toUpperCase().replaceAll(" ", "")), secondary, qty, l, t);
} catch(IllegalArgumentException e){
throw new CREFormatException("Invalid mob name: " + mob, t);
}
}
}
@api(environments={CommandHelperEnvironment.class})
public static class tame_mob extends AbstractFunction {
@Override
public String getName() {
return "tame_mob";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1, 2};
}
@Override
public String docs() {
return "void {[player], entityID} Tames any tameable mob to the specified player. Offline players are"
+ " supported, but this means that partial matches are NOT supported. You must type the players"
+ " name exactly. Setting the player to null will untame the mob. If the entity doesn't exist,"
+ " nothing happens.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREUntameableMobException.class, CRELengthException.class,
CREBadEntityException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
String player = null;
MCPlayer mcPlayer = environment.getEnv(CommandHelperEnvironment.class).GetPlayer();
if (mcPlayer != null) {
player = mcPlayer.getName();
}
Construct entityID = null;
if (args.length == 2) {
if (args[0] instanceof CNull) {
player = null;
} else {
player = args[0].val();
}
entityID = args[1];
} else {
entityID = args[0];
}
MCLivingEntity e = Static.getLivingEntity(entityID, t);
if (e == null) {
return CVoid.VOID;
} else if (e instanceof MCTameable) {
MCTameable mct = ((MCTameable) e);
if (player != null) {
mct.setOwner(Static.getServer().getOfflinePlayer(player));
} else {
mct.setOwner(null);
}
return CVoid.VOID;
} else {
throw new CREUntameableMobException("The specified entity is not tameable", t);
}
}
}
@api
public static class get_mob_owner extends AbstractFunction {
@Override
public String getName() {
return "get_mob_owner";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "string {entityID} Returns the owner's name, or null if the mob is unowned. An UntameableMobException is thrown if"
+ " mob isn't tameable to begin with.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREUntameableMobException.class, CRELengthException.class,
CREBadEntityException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
MCLivingEntity e = Static.getLivingEntity(args[0], t);
if (e == null) {
return CNull.NULL;
} else if (e instanceof MCTameable) {
MCAnimalTamer at = ((MCTameable) e).getOwner();
if (null != at) {
return new CString(at.getName(), t);
} else {
return CNull.NULL;
}
} else {
throw new CREUntameableMobException("The specified entity is not tameable", t);
}
}
}
@api
public static class is_tameable extends AbstractFunction {
@Override
public String getName() {
return "is_tameable";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "boolean {entityID} Returns true or false if the specified entity is tameable";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRELengthException.class, CREBadEntityException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
MCEntity e = Static.getEntity(args[0], t);
boolean ret;
if (e == null) {
ret = false;
} else if (e instanceof MCTameable) {
ret = true;
} else {
ret = false;
}
return CBoolean.get(ret);
}
}
@api(environments={CommandHelperEnvironment.class})
public static class make_effect extends AbstractFunction {
@Override
public String getName() {
return "make_effect";
}
@Override
public Integer[] numArgs() {
return new Integer[]{2, 3};
}
@Override
public String docs() {
return "void {xyzArray, effect, [radius]} Plays the specified effect (sound effect) at the given location, for all players within"
+ " the radius (or 64 by default). The effect can be one of the following: "
+ StringUtils.Join(MCEffect.values(), ", ", ", or ", " or ")
+ ". Additional data can be supplied with the syntax EFFECT:DATA. The RECORD_PLAY effect takes the item"
+ " id of a disc as data, STEP_SOUND takes a blockID and SMOKE takes a direction bit (4 is upwards).";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class, CREFormatException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_1_3;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws ConfigRuntimeException {
MCLocation l = ObjectGenerator.GetGenerator().location(args[0], (env.getEnv(CommandHelperEnvironment.class).GetCommandSender() instanceof MCPlayer ? env.getEnv(CommandHelperEnvironment.class).GetPlayer().getWorld() : null), t);
MCEffect e = null;
String preEff = args[1].val();
int data = 0;
int radius = 64;
if (preEff.contains(":")) {
try {
data = Integer.parseInt(preEff.substring(preEff.indexOf(':') + 1));
} catch (NumberFormatException ex) {
throw new CRECastException("Effect data expected an integer", t);
}
preEff = preEff.substring(0, preEff.indexOf(':'));
}
try {
e = MCEffect.valueOf(preEff.toUpperCase());
} catch (IllegalArgumentException ex) {
throw new CREFormatException("The effect type " + args[1].val() + " is not valid", t);
}
if (e.equals(MCEffect.STEP_SOUND)) {
MCMaterial mat = StaticLayer.GetConvertor().getMaterial(data);
if (mat == null || !mat.isBlock()) {
throw new CREFormatException("This effect requires a valid BlockID", t);
}
}
if (args.length == 3) {
radius = Static.getInt32(args[2], t);
}
l.getWorld().playEffect(l, e, data, radius);
return CVoid.VOID;
}
}
@api
public static class set_entity_health extends AbstractFunction {
@Override
public String getName() {
return "set_entity_health";
}
@Override
public Integer[] numArgs() {
return new Integer[]{2};
}
@Override
public String docs() {
return "void {entityID, healthPercent} Sets the specified entity's health as a percentage,"
+ " where 0 kills it and 100 gives it full health."
+ " An exception is thrown if the entityID doesn't exist or isn't a LivingEntity.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class, CREBadEntityException.class,
CRERangeException.class, CRELengthException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
MCLivingEntity e = Static.getLivingEntity(args[0], t);
double percent = Static.getDouble(args[1], t);
if (percent < 0 || percent > 100) {
throw new CRERangeException("Health was expected to be a percentage between 0 and 100", t);
} else {
e.setHealth(percent / 100.0 * e.getMaxHealth());
}
return CVoid.VOID;
}
}
@api
public static class get_entity_health extends AbstractFunction {
@Override
public String getName() {
return "get_entity_health";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "double {entityID} Returns the entity's health as a percentage of its maximum health."
+ " If the specified entity doesn't exist, or is not a LivingEntity, a format exception is thrown.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRELengthException.class, CREBadEntityException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_0;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
MCLivingEntity e = Static.getLivingEntity(args[0], t);
return new CDouble(e.getHealth() / e.getMaxHealth() * 100.0, t);
}
}
@api(environments={CommandHelperEnvironment.class})
public static class get_server_info extends AbstractFunction {
@Override
public String getName() {
return "get_server_info";
}
@Override
public Integer[] numArgs() {
return new Integer[]{0, 1};
}
@Override
public String docs() {
return "mixed {[value]} Returns various information about server."
+ "If value is set, it should be an integer of one of the following indexes, and only that information for that index"
+ " will be returned. ---- Otherwise if value is not specified (or is -1), it returns an array of"
+ " information with the following pieces of information in the specified index: "
+ "<ul><li>0 - Server name; the name of the server in server.properties.</li>"
+ "<li>1 - API version; The version of the plugin API this server is implementing.</li>"
+ "<li>2 - Server version; The bare version string of the server implementation.</li>"
+ "<li>3 - Allow flight; If true, Minecraft's inbuilt anti fly check is enabled.</li>"
+ "<li>4 - Allow nether; is true, the Nether dimension is enabled</li>"
+ "<li>5 - Allow end; if true, the End is enabled</li>"
+ "<li>6 - World container; The path to the world container.</li>"
+ "<li>7 - Max player limit; returns the player limit.</li>"
+ "<li>8 - Operators; An array of operators on the server.</li>"
+ "<li>9 - Plugins; An array of plugins loaded by the server.</li>"
+ "<li>10 - Online Mode; If true, users are authenticated with Mojang before login</li>"
+ "<li>11 - Server port; Get the game port that the server runs on</li>"
+ "<li>12 - Server IP; Get the IP that the server runs on</li>"
+ "<li>13 - Uptime; The number of milliseconds the server has been running</li>"
+ "<li>14 - gcmax; The maximum amount of memory that the Java virtual machine will attempt to use, in bytes</li>"
+ "<li>15 - gctotal; The total amount of memory in the Java virtual machine, in bytes</li>"
+ "<li>16 - gcfree; The amount of free memory in the Java Virtual Machine, in bytes</li></ul>";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class, CRERangeException.class,
CRENotFoundException.class};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
@Override
public Boolean runAsync() {
return true;
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
MCServer server = StaticLayer.GetServer();
int index = -1;
if (args.length == 0) {
index = -1;
} else if (args.length == 1) {
index = Static.getInt32(args[0], t);
}
if (index < -1 || index > 16) {
throw new CRERangeException(this.getName() + " expects the index to be between -1 and 16 (inclusive)", t);
}
ArrayList<Construct> retVals = new ArrayList<Construct>();
if (index == 0 || index == -1) {
//Server name
retVals.add(new CString(server.getServerName(), t));
}
if (index == 1 || index == -1) {
// API Version
retVals.add(new CString(server.getAPIVersion(), t));
}
if (index == 2 || index == -1) {
// Server Version
retVals.add(new CString(server.getServerVersion(), t));
}
if (index == 3 || index == -1) {
//Allow flight
retVals.add(CBoolean.get(server.getAllowFlight()));
}
if (index == 4 || index == -1) {
//Allow nether
retVals.add(CBoolean.get(server.getAllowNether()));
}
if (index == 5 || index == -1) {
//Allow end
retVals.add(CBoolean.get(server.getAllowEnd()));
}
if (index == 6 || index == -1) {
//World container
retVals.add(new CString(server.getWorldContainer(), t));
}
if (index == 7 || index == -1) {
//Max player limit
retVals.add(new CInt(server.getMaxPlayers(), t));
}
if (index == 8 || index == -1) {
//Array of op's
CArray co = new CArray(t);
List<MCOfflinePlayer> so = server.getOperators();
for (MCOfflinePlayer o : so) {
if (o == null) {
continue;
}
CString os = new CString(o.getName(), t);
co.push(os, t);
}
retVals.add(co);
}
if (index == 9 || index == -1) {
//Array of plugins
CArray co = new CArray(t);
MCPluginManager plugManager = server.getPluginManager();
if (plugManager == null) {
throw new CRENotFoundException(this.getName()
+ " could not receive the server plugins. Are you running in cmdline mode?", t);
}
List<MCPlugin> plugs = plugManager.getPlugins();
for (MCPlugin p : plugs) {
if (p == null) {
continue;
}
CString name = new CString(p.getName(), t);
co.push(name, t);
}
retVals.add(co);
}
if (index == 10 || index == -1) {
//Online Mode
retVals.add(CBoolean.get(server.getOnlineMode()));
}
if (index == 11 || index == -1) {
//Server port
retVals.add(new CInt(server.getPort(), t));
}
if (index == 12 || index == -1) {
//Server Ip
retVals.add(new CString(server.getIp(), t));
}
if (index == 13 || index == -1) {
//Uptime
long uptime = System.currentTimeMillis() - ManagementFactory.getRuntimeMXBean().getStartTime();
retVals.add(new CInt(uptime, t));
}
if (index == 14 || index == -1) {
//gcmax
retVals.add(new CInt((Runtime.getRuntime().maxMemory()), t));
}
if (index == 15 || index == -1) {
//gctotal
retVals.add(new CInt((Runtime.getRuntime().totalMemory()), t));
}
if (index == 16 || index == -1) {
//gcfree
retVals.add(new CInt((Runtime.getRuntime().freeMemory()), t));
}
if (retVals.size() == 1) {
return retVals.get(0);
} else {
CArray ca = new CArray(t);
for (Construct c : retVals) {
ca.push(c, t);
}
return ca;
}
}
}
@api(environments={CommandHelperEnvironment.class})
public static class get_banned_players extends AbstractFunction {
@Override
public String getName() {
return "get_banned_players";
}
@Override
public Integer[] numArgs() {
return new Integer[]{0};
}
@Override
public String docs() {
return "Array {} An array of players banned on the server.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
@Override
public Boolean runAsync() {
return true;
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
MCServer server = StaticLayer.GetServer();
CArray co = new CArray(t);
List<MCOfflinePlayer> so = server.getBannedPlayers();
for (MCOfflinePlayer o : so) {
if (o == null) {
continue;
}
CString os = new CString(o.getName(), t);
co.push(os, t);
}
return co;
}
}
@api(environments={CommandHelperEnvironment.class})
public static class get_whitelisted_players extends AbstractFunction {
@Override
public String getName() {
return "get_whitelisted_players";
}
@Override
public Integer[] numArgs() {
return new Integer[]{0};
}
@Override
public String docs() {
return "Array {} An array of players whitelisted on the server.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{};
}
@Override
public boolean isRestricted() {
return false;
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
@Override
public Boolean runAsync() {
return true;
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws CancelCommandException, ConfigRuntimeException {
MCServer server = StaticLayer.GetServer();
CArray co = new CArray(t);
List<MCOfflinePlayer> so = server.getWhitelistedPlayers();
for (MCOfflinePlayer o : so) {
if (o == null) {
continue;
}
CString os = new CString(o.getName(), t);
co.push(os, t);
}
return co;
}
}
@api(environments={CommandHelperEnvironment.class})
public static class get_spawner_type extends AbstractFunction{
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREFormatException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
MCWorld w = null;
MCPlayer p = environment.getEnv(CommandHelperEnvironment.class).GetPlayer();
if(p != null){
w = p.getWorld();
}
MCLocation location = ObjectGenerator.GetGenerator().location(args[0], w, t);
if(location.getBlock().getState() instanceof MCCreatureSpawner){
String type = ((MCCreatureSpawner)location.getBlock().getState()).getSpawnedType().name();
return new CString(type, t);
} else {
throw new CREFormatException("The block at " + location.toString() + " is not a spawner block", t);
}
}
@Override
public String getName() {
return "get_spawner_type";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "string {locationArray} Gets the spawner type of the specified mob spawner. ----"
+ " Valid types will be one of the mob types.";
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
}
@api(environments={CommandHelperEnvironment.class})
public static class set_spawner_type extends AbstractFunction{
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREFormatException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
MCWorld w = null;
MCPlayer p = environment.getEnv(CommandHelperEnvironment.class).GetPlayer();
if(p != null){
w = p.getWorld();
}
MCLocation location = ObjectGenerator.GetGenerator().location(args[0], w, t);
MCEntityType type;
try {
type = MCEntityType.valueOf(args[1].val().toUpperCase());
} catch (IllegalArgumentException iae) {
throw new CREBadEntityException("Not a registered entity type: " + args[1].val(), t);
}
if(location.getBlock().getState() instanceof MCCreatureSpawner){
((MCCreatureSpawner)location.getBlock().getState()).setSpawnedType(type);
return CVoid.VOID;
} else {
throw new CREFormatException("The block at " + location.toString() + " is not a spawner block", t);
}
}
@Override
public String getName() {
return "set_spawner_type";
}
@Override
public Integer[] numArgs() {
return new Integer[]{2};
}
@Override
public String docs() {
return "void {locationArray, type} Sets the mob spawner type at the location specified. If the location is not a mob spawner,"
+ " or if the type is invalid, a FormatException is thrown. The type may be one of either "
+ StringUtils.Join(MCEntityType.MCVanillaEntityType.values(), ", ", ", or ");
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
}
@api(environments={CommandHelperEnvironment.class})
public static class launch_firework extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREFormatException.class,CRERangeException.class,CREInvalidWorldException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
MCPlayer p = environment.getEnv(CommandHelperEnvironment.class).GetPlayer();
MCWorld w = null;
if(p != null){
w = p.getWorld();
}
MCLocation loc = ObjectGenerator.GetGenerator().location(args[0], w, t);
CArray options;
if(args.length == 2){
options = Static.getArray(args[1], t);
} else {
options = CArray.GetAssociativeArray(t);
}
int strength = 2;
if(options.containsKey("strength")){
strength = Static.getInt32(options.get("strength", t), t);
if (strength < 0 || strength > 128) {
throw new CRERangeException("Strength must be between 0 and 128", t);
}
}
List<MCFireworkEffect> effects = new ArrayList<>();
if(options.containsKey("effects")) {
Construct cEffects = options.get("effects", t);
if(cEffects instanceof CArray){
for(Construct c : ((CArray) cEffects).asList()){
effects.add(ObjectGenerator.GetGenerator().fireworkEffect((CArray) c, t));
}
} else {
throw new CREFormatException("Firework effects must be an array.", t);
}
} else {
effects.add(ObjectGenerator.GetGenerator().fireworkEffect(options, t));
}
MCFirework firework = loc.getWorld().launchFirework(loc, strength, effects);
return new CString(firework.getUniqueId().toString(), t);
}
@Override
public String getName() {
return "launch_firework";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1, 2};
}
@Override
public String docs() {
Class c;
try {
//Since MCColor actually depends on a bukkit server, we don't want to require that for
//the sake of documentation, so we'll build the color list much more carefully.
//Note the false, so we don't actually initialize the class.
c = Class.forName(MCColor.class.getName(), false, this.getClass().getClassLoader());
} catch (ClassNotFoundException ex) {
//Hrm...
Logger.getLogger(Minecraft.class.getName()).log(Level.SEVERE, null, ex);
return "";
}
List<String> names = new ArrayList<String>();
for(Field f : c.getFields()){
if(f.getType() == MCColor.class){
names.add(f.getName());
}
}
return "void {locationArray, [optionsArray]} Launches a firework. The location array specifies where it is launched from,"
+ " and the options array is an associative array described below. All parameters in the associative array are"
+ " optional, and default to the specified values if not set. The default options being set will make it look like"
+ " a normal firework, with a white explosion. ----"
+ " The options array may have the following keys:\n"
+ "{| cellspacing=\"1\" cellpadding=\"1\" border=\"1\" class=\"wikitable\"\n"
+ "! Array key !! Description !! Default\n"
+ "|-\n"
+ "| strength || A number specifying how far up the firework should go || 2\n"
+ "|-\n"
+ "| flicker || A boolean, determining if the firework will flicker\n || false\n"
+ "|-\n"
+ "| trail || A boolean, determining if the firework will leave a trail || true\n"
+ "|-\n"
+ "| colors || An array of colors, or a pipe seperated string of color names (for the named colors only)"
+ " for instance: array('WHITE') or 'WHITE<nowiki>|</nowiki>BLUE'. If you want custom colors, you must use an array, though"
+ " you can still use color names as an item in the array, for instance: array('ORANGE', array(30, 45, 150))."
+ " These colors are used as the primary colors. || 'WHITE'\n"
+ "|-\n"
+ "| fade || An array of colors to be used as the fade colors. This parameter should be formatted the same as"
+ " the colors parameter || array()\n"
+ "|-\n"
+ "| type || An enum value of one of the firework types, one of: " + StringUtils.Join(MCFireworkType.values(), ", ", " or ")
+ " || " + MCFireworkType.BALL.name() + "\n"
+ "|}\n"
+ "The \"named colors\" can be one of: " + StringUtils.Join(names, ", ", " or ");
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
}
@api
public static class send_texturepack extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREPlayerOfflineException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment,
Construct... args) throws ConfigRuntimeException {
MCPlayer p = Static.GetPlayer(args[0], t);
p.sendTexturePack(args[1].val());
return CVoid.VOID;
}
@Override
public String getName() {
return "send_texturepack";
}
@Override
public Integer[] numArgs() {
return new Integer[]{2};
}
@Override
public String docs() {
return "void {player, url} Sends a texturepack URL to the player's client."
+ " If the client has not been requested to change textures in the"
+ " past, they will recieve a confirmation dialog before downloading"
+ " and switching to the new pack. Clients that ignore server textures"
+ " will not recieve the request, so this function will not affect them.";
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
}
@api
public static class send_resourcepack extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CREPlayerOfflineException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment,
Construct... args) throws ConfigRuntimeException {
MCPlayer p = Static.GetPlayer(args[0], t);
p.sendResourcePack(args[1].val());
return CVoid.VOID;
}
@Override
public String getName() {
return "send_resourcepack";
}
@Override
public Integer[] numArgs() {
return new Integer[]{2};
}
@Override
public String docs() {
return "void {player, url} Sends a resourcepack URL to the player's client."
+ " If the client has not been requested to change resources in the"
+ " past, they will recieve a confirmation dialog before downloading"
+ " and switching to the new pack. Clients that ignore server resources"
+ " will not recieve the request, so this function will not affect them.";
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
}
@api
public static class get_ip_bans extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment,
Construct... args) throws ConfigRuntimeException {
MCServer s = Static.getServer();
CArray ret = new CArray(t);
for (String ip : s.getIPBans()) {
ret.push(new CString(ip, t), t);
}
return ret;
}
@Override
public String getName() {
return "get_ip_bans";
}
@Override
public Integer[] numArgs() {
return new Integer[]{0};
}
@Override
public String docs() {
return "array {} Returns an array of entries from banned-ips.txt.";
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
}
@api
public static class set_ip_banned extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment environment,
Construct... args) throws ConfigRuntimeException {
MCServer s = Static.getServer();
String ip = args[0].val();
if (Static.getBoolean(args[1])) {
s.banIP(ip);
} else {
s.unbanIP(ip);
}
return CVoid.VOID;
}
@Override
public String getName() {
return "set_ip_banned";
}
@Override
public Integer[] numArgs() {
return new Integer[]{2};
}
@Override
public String docs() {
return "void {address, banned} If banned is true, address is added to banned-ips.txt,"
+ " if false, the address is removed.";
}
@Override
public CHVersion since() {
return CHVersion.V3_3_1;
}
}
@api
public static class material_info extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class, CREFormatException.class};
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
MCMaterial i = StaticLayer.GetConvertor().getMaterial(Static.getInt32(args[0], t));
CArray ret = CArray.GetAssociativeArray(t);
ret.set("maxStacksize", new CInt(i.getMaxStackSize(), t), t);
ret.set("maxDurability", new CInt(i.getMaxDurability(), t), t);
ret.set("hasGravity", CBoolean.get(i.hasGravity()), t);
ret.set("isBlock", CBoolean.get(i.isBlock()), t);
ret.set("isBurnable", CBoolean.get(i.isBurnable()), t);
ret.set("isEdible", CBoolean.get(i.isEdible()), t);
ret.set("isFlammable", CBoolean.get(i.isFlammable()), t);
ret.set("isOccluding", CBoolean.get(i.isOccluding()), t);
ret.set("isRecord", CBoolean.get(i.isRecord()), t);
ret.set("isSolid", CBoolean.get(i.isSolid()), t);
ret.set("isTransparent", CBoolean.get(i.isTransparent()), t);
return ret;
}
@Override
public String getName() {
return "material_info";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1};
}
@Override
public String docs() {
return "array {int} Returns an array of info about the material.";
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Version since() {
return CHVersion.V3_3_1;
}
}
@api(environments={CommandHelperEnvironment.class})
public static class drop_item extends AbstractFunction {
@Override
public String getName() {
return "drop_item";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1, 2, 3};
}
@Override
public String docs() {
return "int {[player/LocationArray], item, [spawnNaturally]} Drops the specified item stack at the specified player's feet (or "
+ " at an arbitrary Location, if an array is given), and returns its entity id"
+ " Like the vanilla /give command. player defaults to the current player, and qty defaults to 1."
+ " item takes an item array."
+ " spawnNaturally takes a boolean, which forces the way the item will be spawned. If true, the item will be dropped with a random offset.";
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class, CREFormatException.class, CREPlayerOfflineException.class, CREInvalidWorldException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public CHVersion since() {
return CHVersion.V3_2_0;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public Construct exec(Target t, Environment env, Construct... args) throws ConfigRuntimeException {
MCLocation l;
MCItemStack is;
boolean natural;
if (args.length == 1) {
if (env.getEnv(CommandHelperEnvironment.class).GetPlayer() != null) {
l = env.getEnv(CommandHelperEnvironment.class).GetPlayer().getEyeLocation();
natural = false;
} else {
throw new CREPlayerOfflineException("Invalid sender!", t);
}
if (args[0] instanceof CNull) {
return CNull.NULL; // The item is null, this means we are dropping air.
}
is = ObjectGenerator.GetGenerator().item(args[0], t);
} else {
MCPlayer p;
if (args[0] instanceof CArray) {
p = env.getEnv(CommandHelperEnvironment.class).GetPlayer();
l = ObjectGenerator.GetGenerator().location(args[0], (p != null ? p.getWorld() : null), t);
natural = true;
} else {
p = Static.GetPlayer(args[0].val(), t);
Static.AssertPlayerNonNull(p, t);
l = p.getEyeLocation();
natural = false;
}
if (args[1] instanceof CNull) {
return CNull.NULL; // The item is null, this means we are dropping air.
}
is = ObjectGenerator.GetGenerator().item(args[1], t);
}
if (args.length == 3) {
natural = Static.getBoolean(args[2]);
}
MCItem item;
if (natural) {
item = l.getWorld().dropItemNaturally(l, is);
} else {
item = l.getWorld().dropItem(l, is);
}
if (item != null) {
return new CString(item.getUniqueId().toString(), t);
} else {
return CNull.NULL;
}
}
}
@api
public static class shutdown_server extends AbstractFunction implements Optimizable {
@Override
public String getName() {
return "shutdown_server";
}
@Override
public Integer[] numArgs() {
return new Integer[]{0};
}
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return false;
}
@Override
public String docs() {
return "void {} Shuts down the minecraft server instance.";
}
@Override
public Version since() {
return CHVersion.V3_3_1;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws CancelCommandException {
Static.getServer().shutdown();
throw new CancelCommandException("", t);
}
@Override
public Set<OptimizationOption> optimizationOptions() {
return EnumSet.of(
OptimizationOption.TERMINAL
);
}
}
@api
public static class monitor_redstone extends AbstractFunction {
@Override
public Class<? extends CREThrowable>[] thrown() {
return new Class[]{CRECastException.class, CREFormatException.class};
}
@Override
public boolean isRestricted() {
return true;
}
@Override
public Boolean runAsync() {
return null;
}
@Override
public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException {
MCWorld world = null;
MCPlayer p = environment.getEnv(CommandHelperEnvironment.class).GetPlayer();
if(p != null){
world = p.getWorld();
}
MCLocation location = ObjectGenerator.GetGenerator().location(args[0], world, t);
boolean add = true;
if(args.length > 1){
add = Static.getBoolean(args[1]);
}
Map<MCLocation, Boolean> redstoneMonitors = ServerEvents.getRedstoneMonitors();
if(add){
redstoneMonitors.put(location, location.getBlock().isBlockPowered());
} else {
redstoneMonitors.remove(location);
}
return CVoid.VOID;
}
@Override
public String getName() {
return "monitor_redstone";
}
@Override
public Integer[] numArgs() {
return new Integer[]{1, 2};
}
@Override
public String docs() {
return "void {location, [isMonitored]} Sets up a location to be monitored for redstone changes. If a location is monitored,"
+ " it will cause redstone_changed events to be trigged. By default, isMonitored is true, however, setting it to false"
+ " will remove the previously monitored location from the list of monitors.";
}
@Override
public Version since() {
return CHVersion.V3_3_1;
}
}
}