Spellscript
Spellscript is a programming language for Minecraft, to be introduced in Hack/Mine v0.6.4 and planned for eventual status as an independent Minecraft mod. Spellscript allows you to thoroughly mod Minecraft while in-game, no recompilation required. In other words, there's no installation hassle, no fiddling with jars, etc-- just writing or copy/pasting code into an in-game text editor, and you're done. Minecraft objects are represented in Spellscript as first-class objects, just like they are in Java, allowing for all sorts of amazing possibilities...
For a tutorial on how to use Spellscript, see Spellscript Tutorial. For a general-purpose language reference, read on...
Contents |
Spellscript Hooks in Hack/Mine
Hack/Mine outsources a growing amount of its functionality to Spellscript, such that users can thoroughly customize their RPG experience. Currently, all of the spells and item effects are implemented with Spellscript.
Admins may currently employ Spellscript in the following ways:
In-Game Hooks
- Spellscript-Blocks
- Script-Scheduling
External Hooks
- Command-line Access
- Files in the ".minecraft/spells" directory
Language Mechanics
Spellscript is best construed as a healthy mix of Java and Python. Like Java, it is statically typed and features single-inheritance. Like Python, lexical scopes are signified by indentation and the syntax is relatively terse. Where Java would normally require explicit typing, Spellscript does its best to infer type implicitly. Where Python's flexibility allows unsound behavior, Spellscript introduces robustness to keep things sane.
Basic Syntax
As mentioned, lexical scoping is accomplished in Spellscript via indentation. Additionally, statements in Spellscript are delimited with newlines-- semi-colons may be used for single-line snippets of code.
The general structure of control-flow statements is as follows: <control_statement> ':' (<one-liner> | <suite>), where a suite consisted of an indented block of code. Control statements may be any of the following:
<control_statement> => ('while' | 'until') <expression>
('if' | 'elif') <expression>
'else'
'for' (<variable_decl> | <variable>) 'in' <bound1> ['to' <bound2>]
And here are some use cases:
int i = 0
while i < 15:
print "loop!"
i += 1
until i <= 0:
print "more loop!"
i -= 1
if i == 0:
print "yep, i sure is 0!"
elif i > 0:
print "i is positive!"
else:
print "i is negative!"
for i in 0 to 15:
print i
Careful with the looping constructs: as with any non-trivial programming language, getting stuck in an infinite loop is a real possibility.
Type System
Spellscript features classes, strings, lists, and function pointers (which follow a pass-by-reference model), and primitive types (which follow a pass-by-value model). The primitive types are bool, int, float, and double. To understand their usage, it's best to look at some code.
def someFunction(int i, float j):
return i * 2
class MyClass:
def __init__(self):
pass
def someMethod(self):
return 2
def String someOtherMethod(self):
return "Derpalerp!"
bool b = True
int i = 3
float f = 2f
double d = 1d
string s = "Hello world!"
string t = None
MyClass m = MyClass()
int$(int, float) funcPtr = someFunction
A notable distinction from Python is that variables must be declared before they are used. A variable declaration simply goes <type> <name> ['=' <expression>].
Function and class definitions are similar to those of Python; def signifies a function declaration, and class signifies a class declaration. Function return types may optionally be specified (unless it couldn't be inferred, in which case it is required.)
Literals
Possible literals for Spellscript types go as follows:
| Type | Literals |
|---|---|
| bool | True, False |
| int | 0, -15, 2, 0xf931, etc |
| float | 3.1412f, 1f, -3.f, 0F, .1F, etc |
| double | 3.1412, 1., -3d, 0D, etc |
| string | None, "hello world", "oh hai!", "", etc |
Note that all reference-types (strings, class instances, function pointers, etc) may be assigned None.
Operations
The operations available in Spellscript, and their order of precedence, are as follows:
| Operator | Description | Order |
|---|---|---|
| =, +=, -=, *=, /=, %= | Assignment, Compound Assignment | Right-to-Left |
| is, is not, ==, != | Equality | Right-to-Left |
| or | Boolean OR | Left-to-Right |
| and | Boolean AND | Left-to-Right |
| < <= >= > | Comparison | Right-to-Left |
| + - | Addition, Subtraction | Left-to-Right |
| * / % | Multiplication, Division, Modulo | Left-to-Right |
| not x, -x | Inversion, Negation | Right-to-Left |
| ** | Exponentiation | Right-to-Left |
| x.y, x[y], (x) | Method/Field Reference, Array Indexing, Parenthesis | Left-to-Right |
Function Pointers
Function pointers' syntax is highly subject to change at the time of this writing, so if things stop working (or there's any general doubt), consult this section of the wiki for the current syntax.
The type of a function pointer is declared as follows: <param_type> '$' ([<param_type> {',' <param_type>}])
The simplest way to reference a function declaration as a function pointer is to use its name, without parenthesis at the end. However, this will cause a compilation error if the call is ambiguous (suppose the function/method is overloaded)-- thus, to clarify, one can use the following syntax: <function name> ('$' '(' ')' | ['$'] '(' '$' <param_type> {',' '$' <param_type>} ')') Notice how a lack of parameter types requires a '$' to be included before the parenthesis, otherwise it would be ambiguous with a function call. This complexity is part of the reason the syntax may be revised in the future.
Here's some code to demonstrate valid usage:
def globalFunc(int i, int j): return i + j def execute(int$(int, int) func, int a, int b): return func(a, b) print execute(globalFunc($int, $int), 4, 5)
Function pointers can also have variables bound to them, resulting in a function pointer with the newly bound parameters removed. Consider the following:
def globalFunc(int i, int j): return i + j int$(int) ptr = globalFunc$($int, 3) print ptr(4) #7 should be printed to the console
This is implicitly what happens when a function pointer is referenced from a class instance (i.e. the instance is bound to the function pointer as the self parameter).