I'm helping a friend work on a rpg mod in 开发者_如何学JAVAJava, we basically want to give the administrators of the server a chance to provide his/her own formulas to calculate the outcome of some events.
For instance right now we have an event that happens 1% of the time, and the chance increases by 1% every level of the character. How can we make it so that the server admin is able to specify a function based on the character level that returns a chance for that event to happen?
Is there a way to do that safely, since Java has no direct lambda support? Possibly without having to write a tokenizer that parses the string and executes the function?
In php I might use eval to do such a thing (once I sanitized the input) but in Java I'm stumped.
Why not use some scripting language, like Groovy, that can very nicely integrate with Java (you can pass parameters from Java and get the result back to Java). Then you don't have to write your own parser and could also go beyond simple math formulas if the need arose. (Be aware that on the other hand you might have to restrict the use of Groovy, to prevent some security issues, see for instance here how to do this.)
Maybe you should allow only some kind of functions i.e sum of a x
n where x is level After that you can store it as array of pairs a,n
I don't think you will use something like sin. Usually it will be something like ax+b
If the expressions you need to evaluate are limited to relatively simple arithmetic, maybe some string manipulation and object property accessor/method calls, then I would look into using one of the readily available expression language libraries and see if they suffice, such as MVEL or OGNL.
Otherwise, as others have pointed out, if you need more complex behaviour consider full-fledged scripting languages such as Groovy or JRuby.
If you want to avoid a scripting language like Groovy, then you might want to consider using a configuration syntax that permits you to specify name=list-of-values. Let's assume the syntax is:
name = [ "list", "of", "values" ];
With such a syntax, you could specify pairs of values to provide a mapping from character level to probability of event occurring. If the character levels go from 1 to 10 and the default probabilities mirror the character level, then the entry in the configuration file would be:
level_to_probability_mapping = [
# character level --> probability
#----------------------------------
"1", "1",
"2", "2",
"3", "3",
"4", "4",
"5", "5",
"6", "6",
"7", "7",
"8", "8",
"9", "9",
"10", "10",
];
If your config-file syntax supports only name=single-value syntax, then you can emulate name=list-of-values by setting the value to be a string that contains comma-separated items, and then use java.util.StringTokenizer
or String.split()
to break up the string to gain access to the individual items.
A different approach that is much more flexible but dangerous because it allows a person to execute arbitrary code is as follows...
Step 1. Define a Java interface (or abstract base class) that defines one method. For example:
interface ProbabilityCalculator {
public int calculateProbabilityForLevel(int level);
}
Step 2. Write a "default" class that implements that interface using a simple algorithm.
Step 3. Permit administrators to write their own class that implements the above interface, and add a jar file containing that class to the application's CLASSPATH
.
Step 4. Have a variable in the configuration file specify the name of a class that implements the above interface. If this variable is not present in the configuration file, then fall back to using the name of the default implementation class.
Step 5. Use Java's reflection API to create an instance of the class specified in the configuration file, and then invoke its calculateProbabilityForLevel()
method.
If you are not already familiar with Java reflection, then have a look at my Java Reflection Explained Simply presentation to learn what you will need to know.
精彩评论