diff --git a/nb-configuration.xml b/nb-configuration.xml index afa3ab7da..8c5f724a1 100644 --- a/nb-configuration.xml +++ b/nb-configuration.xml @@ -22,5 +22,6 @@ Any value defined here will override the pom.xml file value but is only applicab false 80 none + JDK_1.6 diff --git a/src/main/java/com/laytonsmith/core/Static.java b/src/main/java/com/laytonsmith/core/Static.java index 251df5f9b..5e3d63a14 100644 --- a/src/main/java/com/laytonsmith/core/Static.java +++ b/src/main/java/com/laytonsmith/core/Static.java @@ -516,8 +516,12 @@ public static void SendMessage(final MCCommandSender m, String msg, final Target } p.sendMessage(line); } else { - if (m != null) { - m.sendMessage(line); + if (m != null && m instanceof MCConsoleCommandSender) { + if(msg.matches("(?m).*\033.*")){ + //We have terminal colors, we need to reset them at the end + msg += TermColors.reset(); + } + com.laytonsmith.core.Static.getLogger().log(Level.INFO, msg); } else { System.out.println(line); } diff --git a/src/main/java/com/laytonsmith/core/environments/Environment.java b/src/main/java/com/laytonsmith/core/environments/Environment.java index dba8fbb9f..814e0fdb8 100644 --- a/src/main/java/com/laytonsmith/core/environments/Environment.java +++ b/src/main/java/com/laytonsmith/core/environments/Environment.java @@ -38,6 +38,10 @@ public final T getEnv(Class clazz){ private void addEnv(EnvironmentImpl mixin){ environments.put(mixin.getClass(), mixin); } + + public boolean hasEnv(Class clazz) { + return environments.containsKey(clazz); + } @Override public Environment clone() throws CloneNotSupportedException { diff --git a/src/main/java/com/laytonsmith/core/functions/DataHandling.java b/src/main/java/com/laytonsmith/core/functions/DataHandling.java index f20762b5f..b918fb0ca 100644 --- a/src/main/java/com/laytonsmith/core/functions/DataHandling.java +++ b/src/main/java/com/laytonsmith/core/functions/DataHandling.java @@ -1805,7 +1805,7 @@ public Construct optimize(Target t, Construct... args) throws ConfigCompileExcep } @api - public static class call_proc extends AbstractFunction { + public static class call_proc extends AbstractFunction implements Optimizable { public String getName() { return "call_proc"; @@ -1817,8 +1817,10 @@ public Integer[] numArgs() { public String docs() { return "mixed {proc_name, [var1...]} Dynamically calls a user defined procedure. call_proc(_myProc, 'var1') is the equivalent of" - + " _myProc('var1'), except you could dynamically build the procedure name if need be. This is useful for having callbacks" - + " in procedures. Throws an InvalidProcedureException if the procedure isn't defined."; + + " _myProc('var1'), except you could dynamically build the procedure name if need be. This is useful for dynamic coding," + + " however, closures work best for callbacks. Throws an InvalidProcedureException if the procedure isn't defined. If you are" + + " hardcoding the first parameter, a warning will be issued, because it is much more efficient and safe to directly use" + + " a procedure if you know what its name is beforehand."; } public ExceptionType[] thrown() { @@ -1838,19 +1840,9 @@ public Boolean runAsync() { } public Construct exec(Target t, Environment env, Construct... args) throws ConfigRuntimeException { - return new CVoid(t); - } - - @Override - public Construct execs(Target t, Environment env, Script parent, ParseTree... nodes) { - if (nodes.length < 1) { - throw new ConfigRuntimeException("Expecting at least one argument to call_proc", ExceptionType.InsufficientArgumentsException, t); - } - Construct[] args = new Construct[nodes.length]; - for (int i = 0; i < nodes.length; i++) { - args[i] = parent.seval(nodes[i], env); + if (args.length < 1) { + throw new ConfigRuntimeException("Expecting at least one argument to " + getName(), ExceptionType.InsufficientArgumentsException, t); } - Procedure proc = env.getEnv(GlobalEnv.class).GetProcs().get(args[0].val()); if (proc != null) { List vars = new ArrayList(Arrays.asList(args)); @@ -1867,10 +1859,69 @@ public Construct execs(Target t, Environment env, Script parent, ParseTree... no ExceptionType.InvalidProcedureException, t); } + public Set optimizationOptions() { + return EnumSet.of(OptimizationOption.OPTIMIZE_DYNAMIC); + } + @Override - public boolean useSpecialExec() { - return true; + public ParseTree optimizeDynamic(Target t, List children) throws ConfigCompileException, ConfigRuntimeException { + if(children.size() < 1){ + throw new ConfigRuntimeException("Expecting at least one argument to " + getName(), ExceptionType.InsufficientArgumentsException, t); + } + if(children.get(0).isConst()){ + CHLog.GetLogger().Log(CHLog.Tags.COMPILER, LogLevel.WARNING, "Hardcoding procedure name in " + getName() + ", which is inefficient." + + " Consider calling the procedure directly if the procedure name is known at compile time.", t); + } + return null; + } + + } + + @api + public static class call_proc_array extends call_proc { + + @Override + public Construct exec(Target t, Environment environment, Construct... args) throws ConfigRuntimeException { + CArray ca = Static.getArray(args[1], t); + if(ca.inAssociativeMode()){ + throw new Exceptions.CastException("Expected the array passed to " + getName() + " to be non-associative.", t); + } + Construct [] args2 = new Construct[(int)ca.size() + 1]; + args2[0] = args[0]; + for(int i = 1; i < args2.length; i++){ + args2[i] = ca.get(i - 1); + } + return super.exec(t, environment, args2); + } + + @Override + public String getName() { + return "call_proc_array"; + } + + @Override + public Integer[] numArgs() { + return new Integer[]{2}; + } + + @Override + public String docs() { + return "mixed {proc_name, array} Works like call_proc, but allows for variable or unknown number of arguments to be passed to" + + " a proc. The array parameter is \"flattened\", and call_proc is essentially called. If the array is associative, an" + + " exception is thrown."; + } + + @Override + public CHVersion since() { + return CHVersion.V3_3_1; } + + @Override + public ParseTree optimizeDynamic(Target t, List children) throws ConfigCompileException, ConfigRuntimeException { + //If they hardcode the name, that's fine, because the variables may just be the only thing that's variable. + return null; + } + } @api(environments=CommandHelperEnvironment.class) diff --git a/src/main/java/com/laytonsmith/core/functions/Echoes.java b/src/main/java/com/laytonsmith/core/functions/Echoes.java index e56eb853b..1a9abce38 100644 --- a/src/main/java/com/laytonsmith/core/functions/Echoes.java +++ b/src/main/java/com/laytonsmith/core/functions/Echoes.java @@ -87,7 +87,9 @@ public Set optimizationOptions() { } } - @api(environments={CommandHelperEnvironment.class}) + //Technically it needs CommandHelperEnvironment, but we have special exception handling in case we're running + //in cmdline mode. + @api(environments={}) @noboilerplate public static class msg extends AbstractFunction{ @@ -100,19 +102,16 @@ public Integer[] numArgs() { } public Construct exec(final Target t, Environment env, final Construct... args) throws CancelCommandException, ConfigRuntimeException { - final MCCommandSender p = env.getEnv(CommandHelperEnvironment.class).GetCommandSender(); StringBuilder b = new StringBuilder(); for(int i = 0; i < args.length; i++){ b.append(args[i].val()); } - Static.SendMessage(p, b.toString(), t); -// int start = 0; -// String s = b.toString(); -// while(true){ -// if(start >= s.length()) break; -// p.sendMessage(s.substring(start, start + 100 >= s.length()?s.length():start + 100)); -// start += 100; -// } + if(env.hasEnv(CommandHelperEnvironment.class)){ + final MCCommandSender p = env.getEnv(CommandHelperEnvironment.class).GetCommandSender(); + Static.SendMessage(p, b.toString(), t); + } else { + System.out.println(Static.MCToANSIColors(b.toString())); + } return new CVoid(t); } diff --git a/src/main/resources/docs/Persistance_Network b/src/main/resources/docs/Persistance_Network index 82f162f97..811c42108 100644 --- a/src/main/resources/docs/Persistance_Network +++ b/src/main/resources/docs/Persistance_Network @@ -48,9 +48,14 @@ A note on file based URIs: The file path is specified after two forward slashes, path on unix looks like this: yml:///path/to/file, and an absolute path on windows looks like this: yml://C:/path/to/file (alternatively yml://C:\path\to\file will also work). On all platforms, a relative path would look like this: yml://path/to/file. Additionally, file based -connections are '''always''' going to be much slower, and much less reliable than SQL based +connections are '''usually''' going to be much faster, but less reliable than SQL based connections, so it is HIGHLY recommended that you use SQL connections, if nothing else, using -the zero config SQLite (which is the default). +the zero config SQLite (which is the default). The only case for a file based connection type is +when using frequently read/written data, in which case a subset of your keys may be written +out to a file based protocol. The ser protocol is the fastest and most compact, +but as it stores the data in a lump binary form, it is not (easily) editable by hand, and +is prone to total data corruption in the event of any section of the file being corrupted. +For a full rundown of the speed comparisons, see the chart below. There are special implementation considerations you must take into account if you are writing an external system that integrates with the persistance network, (including if you edit the @@ -237,4 +242,14 @@ ser | 16 ms | 23 ms | 83 ms | 114 ms | 164 K yml | 33 ms | 14 ms | 2.105 sec | 46.640 sec | 112 K -------+--------------------------------------------------------------------------------- + +An important observation that could be made based on this data is that SQLite is +considerably slower than any of the other protocols. This is because SQLite +is less prone to data corruption, and is multiprocess safe. SQLite manages its own +locking and journaling systems, so it is unlikely to corrupt if a bad write +occurs, or if multiple processes are accessing it at once. Due to this, it is the +default storage mechanism, despite its slower runtime. The tradeoff of data protection +vs. script speed vs. interoperability is not something that can be generically decided +in all cases though, so feel free to change defaults as you see fit. Each protocol +has pros and cons, so you must decide which one to use. {{LearningTrail}} diff --git a/src/test/java/com/laytonsmith/core/functions/DataHandlingTest.java b/src/test/java/com/laytonsmith/core/functions/DataHandlingTest.java index 87659ba61..927da7586 100644 --- a/src/test/java/com/laytonsmith/core/functions/DataHandlingTest.java +++ b/src/test/java/com/laytonsmith/core/functions/DataHandlingTest.java @@ -276,6 +276,7 @@ public void testInclude() throws ConfigCompileException, IOException { verify(fakePlayer).sendMessage("hello"); //delete the test file test.delete(); + test.deleteOnExit(); } @Test(timeout = 10000)