chore: move files

This commit is contained in:
55Honey
2022-03-26 21:29:04 +01:00
parent adc106219c
commit 30797110ee
99 changed files with 0 additions and 0 deletions

2
src/LuaEngine/docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
# Ignore the temporary "build" folder.
build

View File

@@ -0,0 +1,19 @@
# Contributing
Eluna uses C for the Lua engine, C++ for the server modifications and system code, Lua for scripting side code and scripts, python for the web documentation generation - but you do not have to be able to code to help.
You can contribute to Eluna in various ways:
* Improve our documentation: [Documentation generation](DOC_GEN.md)
* Create new features or enhance old features: [Eluna source](https://github.com/ElunaLuaEngine/Eluna)
* Notify us about your concerns, problems and needs regarding Eluna: [Issue tracker](https://github.com/ElunaLuaEngine/Eluna/issues)
* Create and improve Lua scripts, systems, releases and guides: [Eluna forum section](https://www.getmangos.eu/forums/forum/118-eluna-lua-engine/)
### Features and documentation
To contribute to the source code and documentation within it, create a pull request for our github repository:
1. [Set up git](https://help.github.com/articles/set-up-git/)
2. [Fork](https://help.github.com/articles/fork-a-repo/) our repository: [Eluna repository](https://github.com/ElunaLuaEngine/Eluna)
3. Create a branch: `git checkout -b mybranch`
4. Make your contribution changes
5. Commit your changes `git commit -a -m "commit message"`
6. Push your commit to github: `git push`
7. Open a [pull request](https://help.github.com/articles/using-pull-requests/)

View File

@@ -0,0 +1,165 @@
# Documentation generation
Eluna uses a custom made documentation generator to create it's [web documentation](http://elunaluaengine.github.io/).
The generator is written in python by Patman. It works by parsing Eluna's source files for comments and then generates the HTML and javascript for the documentation based on them.
This page guides you through generating the web documentation locally and explains the standards of the documentation comments for you to help us improve our documentation. To contribute with your documentation changes, create a [pull request](https://help.github.com/articles/using-pull-requests/)
# Generating locally
- install [python](https://www.python.org/)(2)
- when installing, tick to install the path variable
- you may need restart afterwards for the installation to properly take effect
- install a package manager like [pip](https://pip.pypa.io/en/latest/)
- if you installed pip and it does not work, restart or try easy_install command
- install the dependencies with manager
- [Jinja2](https://pypi.python.org/pypi/Jinja2)
- [typedecorator](https://pypi.python.org/pypi/typedecorator)
- [markdown](https://pypi.python.org/pypi/Markdown)
- Run in cmd `python -m ElunaDoc` when at `\LuaEngine\docs\`
# Documenting
You can document functions in the Eluna source code. To find examples simply open a method header file like `PlayerMethods.h` and see the comments.
## Templates
Here are some basic templates for a function documentation. When defining a parameter or a return value the type and value name are mandatory, unless the parameter type is `...`, which is used for variable arguments; do not include a name in this case.
```c++
/**
* Short description (about 80 characters long).
*
* @param Type paramName
* @return Type returnName
*/
```
```c++
/**
* Short description (about 80 characters long).
*
* @param Type paramName = defaultValue : parameter description
* @return Type returnName : return value description
*/
```
This is a template for a function that takes in different parameters. When defining a parameter or a return value, the type and value name are mandatory.
```c++
/**
* Short description (about 80 characters long).
*
* @proto returnValue = (object)
* @proto returnValue = (x, y, z)
* @param [WorldObject] object = defaultValue : parameter description
* @param float x = defaultValue : parameter description
* @param float y = defaultValue : parameter description
* @param float z = defaultValue : parameter description
* @return Type returnName : return value description
*/
```
## Standard
A documentation comment block will always start with `/**` and end with `*/`.
All lines start with `*` character followed by one space before any content.
The first paragrph is used as a short description of the function/class, so it should be kept to about 80 characters. The other paragraphs can be as long as desired.
All paragraphs in the description (including the first) should start with a capital letter and end with a period.
**Paragraphs must be separated by an empty line**, e.g.:
```c++
/**
* This is a short description (about 80 characters).
*
* Here's another paragraph with more info. NOTE THE EMPTY LINE BETWEEN THE PARAGRAPHS.
* This does need to be short, and this line is still part of the same paragraph because
* there is no empty line.
*/
```
The parameter and return value descriptions should start with a lowercase letter and not end with a period. If more than one sentence is needed, start the *first* without a capital letter and end the *last* without a period.
Any class, enum or function can be referenced (made a link to) with square brackets.
`[Player]` will reference a player. `[WeatherType]` will reference an enum. `[Player:GetName]` will reference a function.
Use correct indentation with documentation comments.
```c++
/**
* Correct indentation.
*/
```
```c++
/**
* Invalid indentation.
*/
```
## Markdown
You can use [markdown](http://pythonhosted.org//Markdown/) in your descriptions.
For syntax see http://daringfireball.net/projects/markdown/syntax and http://pythonhosted.org//Markdown/#differences
```
/**
* Description.
*
* - list item
* - list item
* - list item
*
*
* // Codeblock
* // Code goes here.
* // Note the 4-space indent.
*
*
* `code line`
*
* *italic*
* **bold**
*/
```
**The above markdown code produces the output below:**
Description.
- list item
- list item
- list item
```
// Codeblock
// Code goes here.
// Note the 4-space indent.
```
`code line`
*italic*
**bold**
## Types
Here are some examples of possible types and most commonly used ones:
```
string
uint64
uint32
uint16
uint8
int64
int32
int16
int8
double
float
...
[EnumName]
[Player]
[Creature]
[GameObject]
[Item]
[Unit]
[WorldObject]
[Object]
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,2 @@
*.pyc
.idea

View File

View File

@@ -0,0 +1,174 @@
import os
import shutil
import typing
from jinja2 import Environment, FileSystemLoader
from typedecorator import params, returns
from ElunaDoc.parser import ClassParser, MethodDoc
import glob
import time
@returns([(str, typing.IO)])
@params(search_path=str)
def find_class_files(search_path):
"""Find and open all files containing Eluna class methods in `search_path`.
:param search_path: the path to search for Eluna methods in
:return: a list of all files containing Eluna methods, and the name of their respective classes
"""
# Get the current working dir and switch to the search path.
old_dir = os.getcwd()
os.chdir(search_path)
# Search for all files ending in "Methods.h".
method_file_names = glob.glob('*Methods.h')
# Open each file.
method_files = [open(file_name, 'r') for file_name in method_file_names]
# Go back to where we were before.
os.chdir(old_dir)
return method_files
def make_renderer(template_path, link_parser_factory):
"""Return a function that can be used to render Jinja2 templates from the `template_path` directory."""
# Set up jinja2 environment to load templates from the templates folder.
env = Environment(loader=FileSystemLoader(template_path), extensions=['jinja2.ext.with_'])
def inner(template_name, output_path, level, **kwargs):
env.filters['parse_links'], env.filters['parse_data_type'] = link_parser_factory(level)
template = env.get_template(template_name)
static = make_static(level)
root = make_root(level)
with open('build/' + output_path, 'w') as out:
out.write(template.render(level=level, static=static, root=root, **kwargs))
return inner
def make_static(level):
return lambda file_name: ('../' * level) + 'static/' + file_name
def make_root(level):
return lambda file_name: ('../' * level) + file_name
if __name__ == '__main__':
# Recreate the build folder and copy static files over.
if os.path.exists('build'):
shutil.rmtree('build')
os.mkdir('build')
shutil.copytree('ElunaDoc/static', 'build/static')
# Load up all files with methods we need to parse.
print('Finding Eluna method files...')
class_files = find_class_files('../')
# Parse all the method files.
classes = []
for f in class_files:
print(f'Parsing file {f.name}...')
classes.append(ClassParser.parse_file(f))
f.close()
# Sort the classes so they are in the correct order in lists.
classes.sort(key=lambda c: c.name)
def make_parsers(level):
"""Returns a function that parses content for refs to other classes, methods, or enums,
and automatically inserts the correct link.
"""
# Make lists of all class names and method names.
class_names = []
method_names = []
for class_ in classes:
class_names.append('[' + class_.name + ']')
for method in class_.methods:
method_names.append('[' + class_.name + ':' + method.name + ']')
def link_parser(content):
# Replace all occurrencies of &Class:Function and then &Class with a link to given func or class
for name in method_names:
# Take the [] off the front of the method's name.
full_name = name[1:-1]
# Split "Class:Method" into "Class" and "Method".
class_name, method_name = full_name.split(':')
url = '{}{}/{}.html'.format(('../' * level), class_name, method_name)
# Replace occurrencies of &Class:Method with the url created
content = content.replace(name, '<a class="fn" href="{}">{}</a>'.format(url, full_name))
for name in class_names:
# Take the [] off the front of the class's name.
class_name = name[1:-1]
url = '{}{}/index.html'.format(('../' * level), class_name)
# Replace occurrencies of &Class:Method with the url created
content = content.replace(name, '<a class="mod" href="{}">{}</a>'.format(url, class_name))
return content
# Links to the "Programming in Lua" documentation for each Lua type.
lua_type_documentation = {
'nil': 'http://www.lua.org/pil/2.1.html',
'boolean': 'http://www.lua.org/pil/2.2.html',
'number': 'http://www.lua.org/pil/2.3.html',
'string': 'http://www.lua.org/pil/2.4.html',
'table': 'http://www.lua.org/pil/2.5.html',
'function': 'http://www.lua.org/pil/2.6.html',
'...': 'http://www.lua.org/pil/5.2.html',
}
def data_type_parser(content):
# If the type is a Lua type, return a link to Lua documentation.
if content in lua_type_documentation:
url = lua_type_documentation[content]
return '<strong><a href="{}">{}</a></strong>'.format(url, content)
# Otherwise try to build a link to the proper page.
if content in class_names:
class_name = content[1:-1]
url = '{}{}/index.html'.format(('../' * level), class_name)
return '<strong><a class="mod" href="{}">{}</a></strong>'.format(url, class_name)
# Case for enums to direct to a search on github
enum_name = content[1:-1]
url = 'https://github.com/ElunaLuaEngine/ElunaTrinityWotlk/search?l=cpp&q=%22enum+{}%22&type=Code&utf8=%E2%9C%93'.format(enum_name)
return '<strong><a href="{}">{}</a></strong>'.format(url, enum_name)
# By default we just return the name without the [] around it
return content[1:-1]
return link_parser, data_type_parser
# Create the render function with the template path and parser maker.
render = make_renderer('ElunaDoc/templates', make_parsers)
# Render the index.
render('index.html', 'index.html', level=0, classes=classes)
# Render the search index.
render('search-index.js', 'search-index.js', level=0, classes=classes)
# Render the date.
render('date.js', 'date.js', level=0, currdate=time.strftime("%d/%m/%Y"))
for class_ in classes:
print(f'Rendering pages for class {class_.name}...')
# Make a folder for the class.
os.mkdir('build/' + class_.name)
index_path = '{}/index.html'.format(class_.name)
sidebar_path = '{}/sidebar.js'.format(class_.name)
# Render the class's index page.
render('class.html', index_path, level=1, classes=classes, current_class=class_)
# Render the class's sidebar script.
render('sidebar.js', sidebar_path, level=1, classes=classes, current_class=class_)
# Render each method's page.
for method in class_.methods:
method_path = '{}/{}.html'.format(class_.name, method.name)
render('method.html', method_path, level=1, current_class=class_, current_method=method)

View File

@@ -0,0 +1,333 @@
import re
import typing
import markdown
from typedecorator import params, returns, Nullable
class ParameterDoc(object):
"""The documentation data of a parameter or return value for an Eluna method."""
# The integer ranges that each C++ type is valid for. None means valid for all numbers.
valid_ranges = {
'float': None,
'double': None,
'int': ('-2,147,483,647', '2,147,483,647'), # This should be -32767..32767, but it's pretty safe to assume 32-bit.
'int8': ('-127', '127'),
'uint8': ('0', '255'),
'int16': ('-32,767', '32,767'),
'uint16': ('0', '65,535'),
'int32': ('-2,147,483,647', '2,147,483,647'),
'uint32': ('0', '4,294,967,295'),
'int64': ('-9,223,372,036,854,775,808', '9,223,372,036,854,775,807'),
'uint64': ('0', '18,446,744,073,709,551,615'),
'ObjectGuid': ('0', '18,446,744,073,709,551,615'),
}
@params(self=object, name=Nullable(str), data_type=str, description=str, default_value=Nullable(str))
def __init__(self, name, data_type, description, default_value=None):
"""If `name` is not provided, the Parameter is a returned value instead of a parameter."""
self.name = name
self.data_type = data_type
self.default_value = default_value
if self.data_type == '...':
self.name = '...'
else:
assert(self.name is not None)
if description:
# Capitalize the first letter, add a period, and parse as Markdown.
self.description = '{}{}. '.format(description[0].capitalize(), description[1:])
self.description = markdown.markdown(self.description)
else:
self.description = ''
# If the data type is a C++ number, convert to Lua number and add range info to description.
if self.data_type in self.valid_ranges.keys():
range = ParameterDoc.valid_ranges[self.data_type]
if range:
self.description += '<p><em>Valid numbers</em>: integers from {0} to {1}.</p>'.format(range[0], range[1])
else:
self.description += '<p><em>Valid numbers</em>: all decimal numbers.</p>'
self.data_type = 'number'
elif self.data_type == 'bool':
self.data_type = 'boolean'
elif self.data_type == 'int64' or self.data_type == 'uint64':
self.data_type = '[' + self.data_type + ']'
elif not self.data_type in ['nil', 'boolean', 'number', 'string', 'table', 'function', '...'] and self.data_type[:1] != '[':
print(f"Missing angle brackets [] around the data type name: `{self.data_type}`")
class MethodDoc(object):
"""The documentation data of an Eluna method."""
@params(self=object, name=str, description=str, prototypes=[str], parameters=[ParameterDoc], returned=[ParameterDoc])
def __init__(self, name, description, prototypes, parameters, returned):
self.name = name
self.prototypes = prototypes
self.parameters = parameters
self.returned = returned
# Parse the description as Markdown.
self.description = markdown.markdown(description)
# Pull the first paragraph out of the description as the short description.
self.short_description = self.description.split('</p>')[0][3:]
# If it has a description, it is "documented".
self.documented = self.description != ''
class MangosClassDoc(object):
"""The documentation of a MaNGOS class that has Lua methods."""
@params(self=object, name=str, description=str, methods=[MethodDoc])
def __init__(self, name, description, methods):
self.name = name
# Parse the description as Markdown.
self.description = markdown.markdown(description)
# Pull the first paragraph out of the description as the short description.
self.short_description = self.description.split('</p>')[0][3:]
# Sort the methods by their names.
self.methods = sorted(methods, key=lambda m: m.name)
# If any of our methods are not documented, we aren't fully documented.
for method in methods:
if not method.documented:
self.fully_documented = False
break
else:
self.fully_documented = True
# In the same vein, if any of our methods are documented, we aren't fully *un*documented.
for method in methods:
if method.documented:
self.fully_undocumented = False
break
else:
self.fully_undocumented = True
class ClassParser(object):
"""Parses a file line-by-line and returns methods when enough information is received to build them."""
# Various regular expressions to parse different parts of the doc string.
# There are used to parse the class's description.
class_start_regex = re.compile(r"\s*/\*\*\*") # The start of class documentation, i.e. /***
class_body_regex = re.compile(r"\s*\*\s*(.*)") # The "body", i.e. a * and optionally some descriptive text.
class_end_regex = re.compile(r"\s*\*/") # The end of the comment portion, i.e. */
# These are used to parse method documentation.
start_regex = re.compile(r"\s*/\*\*") # The start of documentation, i.e. /**
body_regex = re.compile(r"\s*\s?\*\s?(.*)") # The "body", i.e. a * and optionally some descriptive text.
# An extra optional space (\s?) was thrown in to make it different from `class_body_regex`.
param_regex = re.compile(r"""\s*\*\s@param\s # The @param tag starts with opt. whitespace followed by "* @param ".
([^\s]+)\s(\w+)? # The data type, a space, and the name of the param.
(?:\s=\s(\w+))? # The default value: a = surrounded by spaces, followed by text.
(?:\s:\s(.+))? # The description: a colon surrounded by spaces, followed by text.
""", re.X)
# This is the same as the @param tag, minus the default value part.
return_regex = re.compile(r"""\s*\*\s@return\s
([\[\]\w]+)\s(\w+)
(?:\s:\s(.+))?
""", re.X)
proto_regex = re.compile(r"""\s*\*\s@proto\s
([\w\s,]+)? # The list of arguments.
(?:=\s)? # An equals sign and a space separate the args and returns.
(?:\(([\w\s,]+)\))? # The list of return values, in parens.
""", re.X)
comment_end_regex = re.compile(r"\s*\*/") # The end of the comment portion, i.e. */
end_regex = re.compile(r"\s*int\s(\w+)\s*\(") # The end of the documentation, i.e. int MethodName(
def __init__(self, class_name):
assert ClassParser.class_body_regex is not ClassParser.body_regex
# The methods that have been parsed.
self.methods = []
# The name of the class being parsed.
self.class_name = class_name
# The description of the class being parsed.
self.class_description = ''
# Reset the parser's state machine.
self.reset()
def reset(self):
# What the last handled regex was, to determine what the next should be.
self.last_regex = None
# These are used to piece together the next `Method`.
self.description = ''
self.params = []
self.returned = []
self.method_name = None
self.prototypes = []
def handle_class_body(self, match):
text = match.group(1)
self.class_description += text + '\n'
def handle_body(self, match):
text = match.group(1)
self.description += text + '\n'
def handle_param(self, match):
data_type, name, default, description = match.group(1), match.group(2), match.group(3), match.group(4)
self.params.append(ParameterDoc(name, data_type, description, default))
def handle_return(self, match):
data_type, name, description = match.group(1), match.group(2), match.group(3)
self.returned.append(ParameterDoc(name, data_type, description))
def handle_proto(self, match):
return_values, parameters = match.group(1), match.group(2)
parameters = ' '+parameters+' ' if parameters else ''
return_values = return_values + '= ' if return_values else ''
if self.class_name == 'Global':
prototype = '{0}{{0}}({1})'.format(return_values, parameters)
else:
prototype = '{0}{1}:{{0}}({2})'.format(return_values, self.class_name, parameters)
self.prototypes.append(prototype)
def handle_end(self, match):
self.method_name = match.group(1)
def make_prototype(parameters):
if parameters != '':
parameters = ' ' + parameters + ' '
if self.class_name == 'Global':
if self.returned:
return_values = ', '.join([param.name for param in self.returned])
prototype = '{0} = {1}({2})'.format(return_values, self.method_name, parameters)
else:
prototype = '{0}({1})'.format(self.method_name, parameters)
else:
if self.returned:
return_values = ', '.join([param.name for param in self.returned])
prototype = '{0} = {1}:{2}({3})'.format(return_values, self.class_name, self.method_name, parameters)
else:
prototype = '{0}:{1}({2})'.format(self.class_name, self.method_name, parameters)
return prototype
# If there's no prototype, make one with all params and returns.
if not self.prototypes:
# A list of all parameters with default values.
params_with_default = []
# The index of the last non-default parameter.
last_non_default_i = 0
# If False, a parameter WITHOUT a default value follows one WITH a default value.
# In this case, don't bother generating prototypes.
simple_order = True
for i, param in enumerate(self.params):
if param.default_value:
params_with_default.append(param)
else:
last_non_default_i = i
if params_with_default:
simple_order = False
if not params_with_default or not simple_order:
# Just generate one prototype with all the parameters.
parameters = ', '.join([param.name for param in self.params])
self.prototypes.append(make_prototype(parameters))
else:
# Generate a prototype for all the non-default parameters,
# then one for each default parameter with all the previous parameters.
for i in range(last_non_default_i, len(self.params)):
parameters = ', '.join([param.name for param in self.params[:i+1]])
self.prototypes.append(make_prototype(parameters))
else:
# Format the method name into each prototype.
self.prototypes = [proto.format(self.method_name) for proto in self.prototypes]
self.methods.append(MethodDoc(self.method_name, self.description, self.prototypes, self.params, self.returned))
# Table of which handler is used to handle each regular expressions.
regex_handlers = {
class_start_regex: None,
class_body_regex: handle_class_body,
class_end_regex: None,
start_regex: None,
body_regex: handle_body,
param_regex: handle_param,
return_regex: handle_return,
proto_regex: handle_proto,
comment_end_regex: None,
end_regex: handle_end,
}
# Table of which regular expressions can follow the last handled regex.
# `body_regex` must always come LAST when used, since it also matches param, return, and comment_end.
next_regexes = {
None: [class_start_regex, start_regex, end_regex],
class_start_regex: [class_end_regex, class_body_regex],
class_body_regex: [class_end_regex, class_body_regex],
class_end_regex: [],
start_regex: [param_regex, return_regex, proto_regex, comment_end_regex, body_regex],
body_regex: [param_regex, return_regex, proto_regex, comment_end_regex, body_regex],
proto_regex: [param_regex, return_regex, proto_regex, comment_end_regex, body_regex],
param_regex: [param_regex, return_regex, comment_end_regex, body_regex],
return_regex: [return_regex, comment_end_regex],
comment_end_regex: [end_regex],
end_regex: [],
}
@returns(Nullable(MethodDoc))
@params(self=object, line=str)
def next_line(self, line):
"""Parse the next line of the file.
This method returns a `Method` when enough data to form a `Method` has been parsed.
Otherwise, it returns None.
"""
# Get the list of expected regular expressions using the last one handled.
valid_regexes = self.next_regexes[self.last_regex]
# Try to find a match.
for regex in valid_regexes:
match = regex.match(line)
if match:
handler = self.regex_handlers[regex]
if handler:
handler(self, match)
# Not every regex has a handler, but keep track of where we are anyway.
self.last_regex = regex
# Break at the first match.
break
else:
# No valid regex was found, reset everything.
self.reset()
@returns(MangosClassDoc)
def to_class_doc(self):
"""Create an instance of `MangosClassDoc` from the parser's data.
Is called by `parse_file` once parsing is finished.
"""
return MangosClassDoc(self.class_name, self.class_description, self.methods)
@staticmethod
@returns(MangosClassDoc)
@params(file=typing.IO)
def parse_file(file):
"""Parse the file `file` into a documented class."""
# Get the class name from "ClassMethods.h" by stripping off "Methods.h".
class_name = file.name[:-len('Methods.h')]
parser = ClassParser(class_name)
line = file.readline()
while line:
parser.next_line(line)
line = file.readline()
return parser.to_class_doc()

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,520 @@
/**
* Copyright 2013 The Rust Project Developers. See the COPYRIGHT
* file at the top-level directory of this distribution and at
* http://rust-lang.org/COPYRIGHT.
*
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
* option. This file may not be copied, modified, or distributed
* except according to those terms.
*/
@font-face {
font-family: 'Fira Sans';
font-style: normal;
font-weight: 400;
src: local('Fira Sans'), url("FiraSans-Regular.woff") format('woff');
}
@font-face {
font-family: 'Fira Sans';
font-style: normal;
font-weight: 500;
src: local('Fira Sans Medium'), url("FiraSans-Medium.woff") format('woff');
}
@font-face {
font-family: 'Source Serif Pro';
font-style: normal;
font-weight: 400;
src: local('Source Serif Pro'), url("SourceSerifPro-Regular.woff") format('woff');
}
@font-face {
font-family: 'Source Serif Pro';
font-style: italic;
font-weight: 400;
src: url("Heuristica-Italic.woff") format('woff');
}
@font-face {
font-family: 'Source Serif Pro';
font-style: normal;
font-weight: 700;
src: local('Source Serif Pro Bold'), url("SourceSerifPro-Bold.woff") format('woff');
}
@font-face {
font-family: 'Source Code Pro';
font-style: normal;
font-weight: 400;
src: local('Source Code Pro'), url("SourceCodePro-Regular.woff") format('woff');
}
@font-face {
font-family: 'Source Code Pro';
font-style: normal;
font-weight: 600;
src: local('Source Code Pro Semibold'), url("SourceCodePro-Semibold.woff") format('woff');
}
@import "normalize.css";
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
/* General structure and fonts */
body {
color: #333;
min-width: 500px;
font: 16px/1.4 "Source Serif Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 0;
position: relative;
padding: 10px 15px 20px 15px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.4em;
}
h3 {
font-size: 1.3em;
}
h1, h2, h3:not(.impl):not(.method), h4:not(.method) {
color: black;
font-weight: 500;
margin: 20px 0 15px 0;
padding-bottom: 6px;
}
h1.fqn {
border-bottom: 1px dashed #D5D5D5;
margin-top: 0;
}
h2, h3:not(.impl):not(.method), h4:not(.method) {
border-bottom: 1px solid #DDDDDD;
}
h3.impl, h3.method, h4.method {
font-weight: 600;
margin-top: 10px;
margin-bottom: 10px;
}
h3.impl, h3.method {
margin-top: 15px;
}
h1, h2, h3, h4, section.sidebar, a.source, .search-input, .content table a, .collapse-toggle {
font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}
ol, ul {
padding-left: 25px;
}
ul ul, ol ul, ul ol, ol ol {
margin-bottom: 0;
}
p {
margin: 0 0 .6em 0;
}
code, pre {
font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", Inconsolata, monospace;
white-space: pre-wrap;
}
.docblock code {
background-color: #F5F5F5;
border-radius: 3px;
/*padding: 0 0.2em;*/
}
pre {
background-color: #F5F5F5;
padding: 14px;
}
.source pre {
padding: 20px;
}
.content.source {
margin-top: 50px;
max-width: none;
overflow: visible;
margin-left: 0px;
min-width: 70em;
}
nav.sub {
font-size: 16px;
text-transform: uppercase;
}
.sidebar {
width: 200px;
position: absolute;
left: 0;
top: 0;
min-height: 100%;
}
.content, nav { max-width: 960px; }
/* Everything else */
.js-only, .hidden { display: none; }
.sidebar {
padding: 10px;
}
.sidebar img {
margin: 20px auto;
display: block;
}
.sidebar .location {
font-size: 17px;
margin: 30px 0 20px 0;
background: #e1e1e1;
text-align: center;
color: #333;
}
.block {
padding: 0 10px;
margin-bottom: 14px;
}
.block h2 {
margin-top: 0;
margin-bottom: 8px;
text-align: center;
}
.block a {
display: block;
text-overflow: ellipsis;
overflow: hidden;
line-height: 15px;
padding: 7px 5px;
font-size: 14px;
font-weight: 300;
transition: border 500ms ease-out;
}
.block a:hover {
background: #F5F5F5;
}
.content {
padding: 15px 0;
}
.content.source pre.rust {
white-space: pre;
overflow: auto;
padding-left: 0;
}
.content pre.line-numbers { float: left; border: none; }
.line-numbers span { color: #c67e2d; }
.line-numbers .line-highlighted {
background-color: #f6fdb0;
}
.content .highlighted {
cursor: pointer;
color: #000 !important;
background-color: #ccc;
}
.content .highlighted a { color: #000 !important; }
.content .highlighted.trait { background-color: #fece7e; }
.content .highlighted.mod { background-color: #afc6e4; }
.content .highlighted.enum { background-color: #b4d1b9; }
.content .highlighted.struct { background-color: #e7b1a0; }
.content .highlighted.fn { background-color: #c6afb3; }
.docblock.short.nowrap {
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.docblock.short p {
overflow: hidden;
text-overflow: ellipsis;
margin: 0;
}
.docblock.short code { white-space: nowrap; }
.docblock h1, .docblock h2, .docblock h3, .docblock h4, .docblock h5 {
border-bottom: 1px solid #DDD;
}
.docblock h1 { font-size: 1.3em; }
.docblock h2 { font-size: 1.15em; }
.docblock h3, .docblock h4, .docblock h5 { font-size: 1em; }
.content .out-of-band {
float: right;
font-size: 23px;
}
.content table {
border-spacing: 0 5px;
border-collapse: separate;
}
.content td { vertical-align: top; }
.content td:first-child { padding-right: 20px; }
.content td p:first-child { margin-top: 0; }
.content td h1, .content td h2 { margin-left: 0; font-size: 1.1em; }
.content .item-list {
list-style-type: none;
padding: 0;
}
.content .item-list li { margin-bottom: 3px; }
.content .multi-column {
-moz-column-count: 5;
-moz-column-gap: 2.5em;
-webkit-column-count: 5;
-webkit-column-gap: 2.5em;
column-count: 5;
column-gap: 2.5em;
}
.content .multi-column li { width: 100%; display: inline-block; }
.content .method {
font-size: 1em;
position: relative;
}
.content .methods .docblock { margin-left: 40px; }
.content .impl-methods .docblock { margin-left: 40px; }
nav {
border-bottom: 1px solid #e0e0e0;
padding-bottom: 10px;
margin-bottom: 10px;
}
nav.main {
padding: 20px 0;
text-align: center;
}
nav.main .current {
border-top: 1px solid #000;
border-bottom: 1px solid #000;
}
nav.main .separator {
border: 1px solid #000;
display: inline-block;
height: 23px;
margin: 0 20px;
}
nav.sum { text-align: right; }
nav.sub form { display: inline; }
nav, .content {
margin-left: 230px;
}
a {
text-decoration: none;
color: #000;
background: transparent;
}
p a { color: #4e8bca; }
p a:hover { text-decoration: underline; }
.content a.trait, .block a.current.trait { color: #ed9603; }
.content a.mod, .block a.current.mod { color: #4d76ae; }
.content a.enum, .block a.current.enum { color: #5e9766; }
.content a.struct, .block a.current.struct { color: #e53700; }
.content a.fn, .block a.current.fn { color: #8c6067; }
.content .fnname { color: #8c6067; }
.search-input {
width: 100%;
/* Override Normalize.css: we have margins and do
not want to overflow - the `moz` attribute is necessary
until Firefox 29, too early to drop at this point */
-moz-box-sizing: border-box !important;
box-sizing: border-box !important;
outline: none;
border: none;
border-radius: 1px;
color: #555;
margin-top: 5px;
padding: 10px 16px;
font-size: 17px;
box-shadow: 0 0 0 1px #e0e0e0, 0 0 0 2px transparent;
transition: border-color 300ms ease;
transition: border-radius 300ms ease-in-out;
transition: box-shadow 300ms ease-in-out;
}
.search-input:focus {
border-color: #66afe9;
border-radius: 2px;
border: 0;
outline: 0;
box-shadow: 0 0 8px #078dd8;
}
.search-results .desc {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
display: block;
}
#help {
background: #e9e9e9;
border-radius: 4px;
box-shadow: 0 0 6px rgba(0,0,0,.2);
position: absolute;
top: 300px;
left: 50%;
margin-top: -125px;
margin-left: -275px;
width: 550px;
height: 300px;
border: 1px solid #bfbfbf;
}
#help dt {
float: left;
border-radius: 3px;
border: 1px solid #bfbfbf;
background: #fff;
width: 23px;
text-align: center;
clear: left;
display: block;
margin-top: -1px;
}
#help dd { margin: 5px 33px; }
#help .infos { padding-left: 0; }
#help h1 { margin-top: 0; }
#help div {
width: 50%;
float: left;
padding: 20px;
}
.stability {
border-left: 6px solid;
padding: 3px 6px;
border-radius: 3px;
}
h1 .stability {
text-transform: lowercase;
font-weight: 400;
margin-left: 14px;
padding: 4px 10px;
}
.impl-methods .stability, .methods .stability {
margin-right: 20px;
}
.stability.Deprecated { border-color: #A071A8; color: #82478C; }
.stability.Experimental { border-color: #D46D6A; color: #AA3C39; }
.stability.Unstable { border-color: #D4B16A; color: #AA8439; }
.stability.Stable { border-color: #54A759; color: #2D8632; }
.stability.Frozen { border-color: #009431; color: #007726; }
.stability.Locked { border-color: #0084B6; color: #00668c; }
.stability.Unmarked { border-color: #BBBBBB; }
.summary {
padding-right: 0px;
}
.summary.Deprecated { background-color: #A071A8; }
.summary.Experimental { background-color: #D46D6A; }
.summary.Unstable { background-color: #D4B16A; }
.summary.Stable { background-color: #54A759; }
.summary.Unmarked { background-color: #BBBBBB; }
:target { background: #FDFFD3; }
/* Code highlighting */
pre.rust .kw { color: #8959A8; }
pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; }
pre.rust .number, pre.rust .string { color: #718C00; }
pre.rust .self, pre.rust .boolval, pre.rust .prelude-val,
pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; }
pre.rust .comment { color: #8E908C; }
pre.rust .doccomment { color: #4D4D4C; }
pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; }
pre.rust .lifetime { color: #B76514; }
.rusttest { display: none; }
pre.rust { position: relative; }
.test-arrow {
display: inline-block;
position: absolute;
top: 0;
right: 10px;
font-size: 150%;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
}
.methods .section-header {
/* Override parent class attributes. */
border-bottom: none !important;
font-size: 1.1em !important;
margin: 0 0 -5px;
padding: 0;
}
.section-header:hover a:after {
content: '\2002\00a7\2002';
}
/* Media Queries */
@media (max-width: 700px) {
.sidebar {
display: none;
}
.content {
margin-left: 0px;
}
nav.sub {
margin: 0 auto;
}
}
.collapse-toggle {
font-weight: 100;
position: absolute;
left: 13px;
color: #999;
margin-top: 2px;
}
.toggle-wrapper > .collapse-toggle {
left: -24px;
margin-top: 0px;
}
.toggle-wrapper {
position: relative;
}
.toggle-wrapper.collapsed {
height: 1em;
transition: height .2s;
}
.collapse-toggle > .inner {
display: inline-block;
width: 1ch;
text-align: center;
}
.toggle-label {
color: #999;
font-style: italic;
}

View File

@@ -0,0 +1,782 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
/*jslint browser: true, es5: true */
/*globals $: true, rootPath: true */
(function() {
"use strict";
var resizeTimeout, interval;
$('.js-only').removeClass('js-only');
function getQueryStringParams() {
var params = {};
window.location.search.substring(1).split("&").
map(function(s) {
var pair = s.split("=");
params[decodeURIComponent(pair[0])] =
typeof pair[1] === "undefined" ?
null : decodeURIComponent(pair[1]);
});
return params;
}
function browserSupportsHistoryApi() {
return window.history && typeof window.history.pushState === "function";
}
function resizeShortBlocks() {
if (resizeTimeout) {
clearTimeout(resizeTimeout);
}
resizeTimeout = setTimeout(function() {
var contentWidth = $('.content').width();
$('.docblock.short').width(function() {
return contentWidth - 40 - $(this).prev().width();
}).addClass('nowrap');
$('.summary-column').width(function() {
return contentWidth - 40 - $(this).prev().width();
})
}, 150);
}
resizeShortBlocks();
$(window).on('resize', resizeShortBlocks);
function highlightSourceLines() {
var i, from, to, match = window.location.hash.match(/^#?(\d+)(?:-(\d+))?$/);
if (match) {
from = parseInt(match[1], 10);
to = Math.min(50000, parseInt(match[2] || match[1], 10));
from = Math.min(from, to);
if ($('#' + from).length === 0) {
return;
}
$('#' + from)[0].scrollIntoView();
$('.line-numbers span').removeClass('line-highlighted');
for (i = from; i <= to; ++i) {
$('#' + i).addClass('line-highlighted');
}
}
}
highlightSourceLines();
$(window).on('hashchange', highlightSourceLines);
$(document).on('keyup', function(e) {
if (document.activeElement.tagName === 'INPUT') {
return;
}
if (e.which === 191 && $('#help').hasClass('hidden')) { // question mark
e.preventDefault();
$('#help').removeClass('hidden');
} else if (e.which === 27) { // esc
if (!$('#help').hasClass('hidden')) {
e.preventDefault();
$('#help').addClass('hidden');
} else if (!$('#search').hasClass('hidden')) {
e.preventDefault();
$('#search').addClass('hidden');
$('#main').removeClass('hidden');
}
} else if (e.which === 83) { // S
e.preventDefault();
$('.search-input').focus();
}
}).on('click', function(e) {
if (!$(e.target).closest('#help').length) {
$('#help').addClass('hidden');
}
});
$('.version-selector').on('change', function() {
var i, match,
url = document.location.href,
stripped = '',
len = rootPath.match(/\.\.\//g).length + 1;
for (i = 0; i < len; ++i) {
match = url.match(/\/[^\/]*$/);
if (i < len - 1) {
stripped = match[0] + stripped;
}
url = url.substring(0, url.length - match[0].length);
}
url += '/' + $('.version-selector').val() + stripped;
document.location.href = url;
});
/**
* A function to compute the Levenshtein distance between two strings
* Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported
* Full License can be found at http://creativecommons.org/licenses/by-sa/3.0/legalcode
* This code is an unmodified version of the code written by Marco de Wit
* and was found at http://stackoverflow.com/a/18514751/745719
*/
var levenshtein = (function() {
var row2 = [];
return function(s1, s2) {
if (s1 === s2) {
return 0;
} else {
var s1_len = s1.length, s2_len = s2.length;
if (s1_len && s2_len) {
var i1 = 0, i2 = 0, a, b, c, c2, row = row2;
while (i1 < s1_len)
row[i1] = ++i1;
while (i2 < s2_len) {
c2 = s2.charCodeAt(i2);
a = i2;
++i2;
b = i2;
for (i1 = 0; i1 < s1_len; ++i1) {
c = a + (s1.charCodeAt(i1) !== c2 ? 1 : 0);
a = row[i1];
b = b < a ? (b < c ? b + 1 : c) : (a < c ? a + 1 : c);
row[i1] = b;
}
}
return b;
} else {
return s1_len + s2_len;
}
}
};
})();
function initSearch(rawSearchIndex) {
var currentResults, index, searchIndex;
var MAX_LEV_DISTANCE = 3;
var params = getQueryStringParams();
// Populate search bar with query string search term when provided,
// but only if the input bar is empty. This avoid the obnoxious issue
// where you start trying to do a search, and the index loads, and
// suddenly your search is gone!
if ($(".search-input")[0].value === "") {
$(".search-input")[0].value = params.search || '';
}
/**
* Executes the query and builds an index of results
* @param {[Object]} query [The user query]
* @param {[type]} max [The maximum results returned]
* @param {[type]} searchWords [The list of search words to query
* against]
* @return {[type]} [A search index of results]
*/
function execQuery(query, max, searchWords) {
var valLower = query.query.toLowerCase(),
val = valLower,
typeFilter = itemTypeFromName(query.type),
results = [],
split = valLower.split("::");
//remove empty keywords
for (var j = 0; j < split.length; ++j) {
split[j].toLowerCase();
if (split[j] === "") {
split.splice(j, 1);
}
}
// quoted values mean literal search
var nSearchWords = searchWords.length;
if ((val.charAt(0) === "\"" || val.charAt(0) === "'") &&
val.charAt(val.length - 1) === val.charAt(0))
{
val = val.substr(1, val.length - 2);
for (var i = 0; i < nSearchWords; ++i) {
if (searchWords[i] === val) {
// filter type: ... queries
if (typeFilter < 0 || typeFilter === searchIndex[i].ty) {
results.push({id: i, index: -1});
}
}
if (results.length === max) {
break;
}
}
} else {
// gather matching search results up to a certain maximum
val = val.replace(/\_/g, "");
for (var i = 0; i < split.length; ++i) {
for (var j = 0; j < nSearchWords; ++j) {
var lev_distance;
if (searchWords[j].indexOf(split[i]) > -1 ||
searchWords[j].indexOf(val) > -1 ||
searchWords[j].replace(/_/g, "").indexOf(val) > -1)
{
// filter type: ... queries
if (typeFilter < 0 || typeFilter === searchIndex[j].ty) {
results.push({
id: j,
index: searchWords[j].replace(/_/g, "").indexOf(val),
lev: 0,
});
}
} else if (
(lev_distance = levenshtein(searchWords[j], val)) <=
MAX_LEV_DISTANCE) {
if (typeFilter < 0 || typeFilter === searchIndex[j].ty) {
results.push({
id: j,
index: 0,
// we want lev results to go lower than others
lev: lev_distance,
});
}
}
if (results.length === max) {
break;
}
}
}
}
var nresults = results.length;
for (var i = 0; i < nresults; ++i) {
results[i].word = searchWords[results[i].id];
results[i].item = searchIndex[results[i].id] || {};
}
// if there are no results then return to default and fail
if (results.length === 0) {
return [];
}
results.sort(function(aaa, bbb) {
var a, b;
// Sort by non levenshtein results and then levenshtein results by the distance
// (less changes required to match means higher rankings)
a = (aaa.lev);
b = (bbb.lev);
if (a !== b) return a - b;
// sort by crate (non-current crate goes later)
a = (aaa.item.crate !== window.currentCrate);
b = (bbb.item.crate !== window.currentCrate);
if (a !== b) return a - b;
// sort by exact match (mismatch goes later)
a = (aaa.word !== valLower);
b = (bbb.word !== valLower);
if (a !== b) return a - b;
// sort by item name length (longer goes later)
a = aaa.word.length;
b = bbb.word.length;
if (a !== b) return a - b;
// sort by item name (lexicographically larger goes later)
a = aaa.word;
b = bbb.word;
if (a !== b) return (a > b ? +1 : -1);
// sort by index of keyword in item name (no literal occurrence goes later)
a = (aaa.index < 0);
b = (bbb.index < 0);
if (a !== b) return a - b;
// (later literal occurrence, if any, goes later)
a = aaa.index;
b = bbb.index;
if (a !== b) return a - b;
// sort by description (no description goes later)
a = (aaa.item.desc === '');
b = (bbb.item.desc === '');
if (a !== b) return a - b;
// sort by type (later occurrence in `itemTypes` goes later)
a = aaa.item.ty;
b = bbb.item.ty;
if (a !== b) return a - b;
// sort by path (lexicographically larger goes later)
a = aaa.item.path;
b = bbb.item.path;
if (a !== b) return (a > b ? +1 : -1);
// que sera, sera
return 0;
});
// remove duplicates, according to the data provided
for (var i = results.length - 1; i > 0; i -= 1) {
if (results[i].word === results[i - 1].word &&
results[i].item.ty === results[i - 1].item.ty &&
results[i].item.path === results[i - 1].item.path)
{
results[i].id = -1;
}
}
for (var i = 0; i < results.length; ++i) {
var result = results[i],
name = result.item.name.toLowerCase(),
path = result.item.path.toLowerCase(),
parent = result.item.parent;
var valid = validateResult(name, path, split, parent);
if (!valid) {
result.id = -1;
}
}
return results;
}
/**
* Validate performs the following boolean logic. For example:
* "File::open" will give IF A PARENT EXISTS => ("file" && "open")
* exists in (name || path || parent) OR => ("file" && "open") exists in
* (name || path )
*
* This could be written functionally, but I wanted to minimise
* functions on stack.
*
* @param {[string]} name [The name of the result]
* @param {[string]} path [The path of the result]
* @param {[string]} keys [The keys to be used (["file", "open"])]
* @param {[object]} parent [The parent of the result]
* @return {[boolean]} [Whether the result is valid or not]
*/
function validateResult(name, path, keys, parent) {
for (var i=0; i < keys.length; ++i) {
// each check is for validation so we negate the conditions and invalidate
if (!(
// check for an exact name match
name.toLowerCase().indexOf(keys[i]) > -1 ||
// then an exact path match
path.toLowerCase().indexOf(keys[i]) > -1 ||
// next if there is a parent, check for exact parent match
(parent !== undefined &&
parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
// lastly check to see if the name was a levenshtein match
levenshtein(name.toLowerCase(), keys[i]) <=
MAX_LEV_DISTANCE)) {
return false;
}
}
return true;
}
function getQuery() {
var matches, type, query = $('.search-input').val();
matches = query.match(/^(fn|mod|str(uct)?|enum|trait|t(ype)?d(ef)?)\s*:\s*/i);
if (matches) {
type = matches[1].replace(/^td$/, 'typedef')
.replace(/^str$/, 'struct')
.replace(/^tdef$/, 'typedef')
.replace(/^typed$/, 'typedef');
query = query.substring(matches[0].length);
}
return {
query: query,
type: type,
id: query + type,
};
}
function initSearchNav() {
var hoverTimeout, $results = $('.search-results .result');
$results.on('click', function() {
var dst = $(this).find('a')[0];
if (window.location.pathname == dst.pathname) {
$('#search').addClass('hidden');
$('#main').removeClass('hidden');
}
document.location.href = dst.href;
}).on('mouseover', function() {
var $el = $(this);
clearTimeout(hoverTimeout);
hoverTimeout = setTimeout(function() {
$results.removeClass('highlighted');
$el.addClass('highlighted');
}, 20);
});
$(document).off('keydown.searchnav');
$(document).on('keydown.searchnav', function(e) {
var $active = $results.filter('.highlighted');
if (e.which === 38) { // up
e.preventDefault();
if (!$active.length || !$active.prev()) {
return;
}
$active.prev().addClass('highlighted');
$active.removeClass('highlighted');
} else if (e.which === 40) { // down
e.preventDefault();
if (!$active.length) {
$results.first().addClass('highlighted');
} else if ($active.next().length) {
$active.next().addClass('highlighted');
$active.removeClass('highlighted');
}
} else if (e.which === 13) { // return
e.preventDefault();
if ($active.length) {
document.location.href = $active.find('a').prop('href');
}
}
});
}
function escape(content) {
return $('<h1/>').text(content).html();
}
function showResults(results) {
var output, shown, query = getQuery();
currentResults = query.id;
output = '<h1>Results for ' + escape(query.query) +
(query.type ? ' (type: ' + escape(query.type) + ')' : '') + '</h1>';
output += '<table class="search-results">';
if (results.length > 0) {
shown = [];
results.forEach(function(item) {
var name, type;
if (shown.indexOf(item) !== -1) {
return;
}
shown.push(item);
name = item.name;
type = itemTypes[item.ty];
output += '<tr class="' + type + ' result"><td>';
if (type === 'mod') {
output += item.path +
'::<a href="' + rootPath +
item.path.replace(/::/g, '/') + // '/' +
name + '/index.html" class="' +
type + '">' + name + '</a>';
} else if (type === 'static' || type === 'reexport') {
output += item.path +
'::<a href="' + rootPath +
item.path.replace(/::/g, '/') +
'/index.html" class="' + type +
'">' + name + '</a>';
} else if (item.parent !== undefined) {
var myparent = item.parent;
var anchor = '#' + type + '.' + name;
output += item.path + '::' + myparent.name +
'::<a href="' + rootPath +
item.path.replace(/::/g, '/') +
'/' + myparent.name +
'.html' + anchor +
'" class="' + type +
'">' + name + '</a>';
} else {
output += item.path +
'::<a href="' + rootPath +
item.path.replace(/::/g, '/') +
'/' + name +
'.html" class="' + type +
'">' + name + '</a>';
}
output += '</td><td><span class="desc">' + item.desc +
'</span></td></tr>';
});
} else {
output += 'No results - Request function at <a href="https://github.com/ElunaLuaEngine/Eluna/issues">Eluna issue tracker</a>';
}
output += "</p>";
$('#main.content').addClass('hidden');
$('#search.content').removeClass('hidden').html(output);
$('#search .desc').width($('#search').width() - 40 -
$('#search td:first-child').first().width());
initSearchNav();
}
function search(e) {
var query,
filterdata = [],
obj, i, len,
results = [],
maxResults = 200,
resultIndex;
var params = getQueryStringParams();
query = getQuery();
if (e) {
e.preventDefault();
}
if (!query.query || query.id === currentResults) {
return;
}
// Because searching is incremental by character, only the most
// recent search query is added to the browser history.
// Do not do this on local due to chrome security errors.
// http://stackoverflow.com/a/32454237/3586583
if (browserSupportsHistoryApi() && window.location.protocol != "file:") {
if (!history.state && !params.search) {
history.pushState(query, "", "?search=" +
encodeURIComponent(query.query));
} else {
history.replaceState(query, "", "?search=" +
encodeURIComponent(query.query));
}
}
resultIndex = execQuery(query, 20000, index);
len = resultIndex.length;
for (i = 0; i < len; ++i) {
if (resultIndex[i].id > -1) {
obj = searchIndex[resultIndex[i].id];
filterdata.push([obj.name, obj.ty, obj.path, obj.desc]);
results.push(obj);
}
if (results.length >= maxResults) {
break;
}
}
showResults(results);
}
// This mapping table should match the discriminants of
// `rustdoc::html::item_type::ItemType` type in Rust.
var itemTypes = ["mod",
"struct",
"type",
"fn",
"type",
"static",
"trait",
"impl",
"viewitem",
"tymethod",
"method",
"structfield",
"variant",
"ffi",
"ffs",
"macro",
"primitive"];
function itemTypeFromName(typename) {
for (var i = 0; i < itemTypes.length; ++i) {
if (itemTypes[i] === typename) return i;
}
return -1;
}
function buildIndex(rawSearchIndex) {
searchIndex = [];
var searchWords = [];
for (var crate in rawSearchIndex) {
if (!rawSearchIndex.hasOwnProperty(crate)) { continue }
// an array of [(Number) item type,
// (String) name,
// (String) full path or empty string for previous path,
// (String) description,
// (optional Number) the parent path index to `paths`]
var items = rawSearchIndex[crate].items;
// an array of [(Number) item type,
// (String) name]
var paths = rawSearchIndex[crate].paths;
// convert `paths` into an object form
var len = paths.length;
for (var i = 0; i < len; ++i) {
paths[i] = {ty: paths[i][0], name: paths[i][1]};
}
// convert `items` into an object form, and construct word indices.
//
// before any analysis is performed lets gather the search terms to
// search against apart from the rest of the data. This is a quick
// operation that is cached for the life of the page state so that
// all other search operations have access to this cached data for
// faster analysis operations
var len = items.length;
var lastPath = "";
for (var i = 0; i < len; ++i) {
var rawRow = items[i];
var row = {crate: crate, ty: rawRow[0], name: rawRow[1],
path: rawRow[2] || lastPath, desc: rawRow[3],
parent: paths[rawRow[4]]};
searchIndex.push(row);
if (typeof row.name === "string") {
var word = row.name.toLowerCase();
searchWords.push(word);
} else {
searchWords.push("");
}
lastPath = row.path;
}
}
return searchWords;
}
function startSearch() {
var keyUpTimeout;
$('.do-search').on('click', search);
$('.search-input').on('keyup', function() {
clearTimeout(keyUpTimeout);
keyUpTimeout = setTimeout(search, 100);
});
// Push and pop states are used to add search results to the browser
// history.
if (browserSupportsHistoryApi()) {
$(window).on('popstate', function(e) {
var params = getQueryStringParams();
// When browsing back from search results the main page
// visibility must be reset.
if (!params.search) {
$('#main.content').removeClass('hidden');
$('#search.content').addClass('hidden');
}
// When browsing forward to search results the previous
// search will be repeated, so the currentResults are
// cleared to ensure the search is successful.
currentResults = null;
// Synchronize search bar with query string state and
// perform the search. This will empty the bar if there's
// nothing there, which lets you really go back to a
// previous state with nothing in the bar.
$('.search-input').val(params.search);
// Some browsers fire 'onpopstate' for every page load
// (Chrome), while others fire the event only when actually
// popping a state (Firefox), which is why search() is
// called both here and at the end of the startSearch()
// function.
search();
});
}
search();
}
index = buildIndex(rawSearchIndex);
startSearch();
// Draw a convenient sidebar of known crates if we have a listing
if (rootPath == '../') {
var sidebar = $('.sidebar');
var div = $('<div>').attr('class', 'block crate');
div.append($('<h2>').text('All Classes'));
var crates = [];
for (var crate in rawSearchIndex) {
if (!rawSearchIndex.hasOwnProperty(crate)) { continue }
crates.push(crate);
}
crates.sort();
for (var i = 0; i < crates.length; ++i) {
var klass = 'crate';
if (crates[i] == window.currentCrate) {
klass += ' current';
}
div.append($('<a>', {'href': '../' + crates[i] + '/index.html',
'class': klass}).text(crates[i]));
}
sidebar.append(div);
}
}
window.initSearch = initSearch;
window.register_implementors = function(imp) {
var list = $('#implementors-list');
var libs = Object.getOwnPropertyNames(imp);
for (var i = 0; i < libs.length; ++i) {
if (libs[i] == currentCrate) continue;
var structs = imp[libs[i]];
for (var j = 0; j < structs.length; ++j) {
var code = $('<code>').append(structs[j]);
$.each(code.find('a'), function(idx, a) {
var href = $(a).attr('href');
if (!href.startsWith('http')) {
$(a).attr('href', rootPath + $(a).attr('href'));
}
});
var li = $('<li>').append(code);
list.append(li);
}
}
};
if (window.pending_implementors) {
window.register_implementors(window.pending_implementors);
}
// See documentation in html/render.rs for what this is doing.
var query = getQueryStringParams();
if (query['gotosrc']) {
window.location = $('#src-' + query['gotosrc']).attr('href');
}
$("#expand-all").on("click", function() {
$(".docblock").show();
$(".toggle-label").hide();
$(".toggle-wrapper").removeClass("collapsed");
$(".collapse-toggle").children(".inner").html("-");
});
$("#collapse-all").on("click", function() {
$(".docblock").hide();
$(".toggle-label").show();
$(".toggle-wrapper").addClass("collapsed");
$(".collapse-toggle").children(".inner").html("+");
});
$(document).on("click", ".collapse-toggle", function() {
var toggle = $(this);
var relatedDoc = toggle.parent().next();
if (relatedDoc.is(".docblock")) {
if (relatedDoc.is(":visible")) {
relatedDoc.slideUp({duration:'fast', easing:'linear'});
toggle.parent(".toggle-wrapper").addClass("collapsed");
toggle.children(".inner").html("+");
toggle.children(".toggle-label").fadeIn();
} else {
relatedDoc.slideDown({duration:'fast', easing:'linear'});
toggle.parent(".toggle-wrapper").removeClass("collapsed");
toggle.children(".inner").html("-");
toggle.children(".toggle-label").hide();
}
}
});
$(function() {
var toggle = "<a href='javascript:void(0)'"
+ "class='collapse-toggle'>[<span class='inner'>-</span>]</a>";
$(".method").each(function() {
if ($(this).next().is(".docblock")) {
$(this).children().first().after(toggle);
}
});
var mainToggle = $(toggle);
mainToggle.append("<span class='toggle-label' style='display:none'>"
+ "&nbsp;Expand&nbsp;description</span></a>")
var wrapper = $("<div class='toggle-wrapper'>");
wrapper.append(mainToggle);
$("#main > .docblock").before(wrapper);
});
}());

View File

@@ -0,0 +1 @@
/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}

View File

@@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="{% block description %}API documentation for the Eluna engine.{% endblock %}">
<meta name="keywords" content="eluna, lua, lua engine, trinitycore, trinity, mangos, cmangos, script, scripting, doc, docs, documentation">
<title>{% block title %}Eluna API{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="{{ static('main.css') }}">
<link rel="shortcut icon" href="{{ static('favicon.ico') }}">
</head>
<body class="rustdoc">
<!--[if lte IE 8]>
<div class="warning">
This old browser is unsupported and will most likely display funky
things.
</div>
<![endif]-->
<section class="sidebar">
<a href='{{ root('index.html') }}'><img src='{{ static('eluna-logo.png') }}' alt='Eluna Logo' width='100'></a>
<div class="block">
{% block sidebar %}
<h2>All Classes</h2>
{% for class in classes -%}
<a class="mod {{ 'current' if class == current_class }}" href="{{ root(class.name + '/index.html') }}">{{ class.name }}</a>
{%- endfor %}
{% endblock %}
</div>
</section>
<nav class="sub">
<form class="search-form js-only">
<div class="search-container">
<input class="search-input" name="search"
autocomplete="off"
placeholder="Click or press 'S' to search, '?' for more options..."
type="search">
</div>
</form>
</nav>
<section id='main' class="content mod">
<h1 class='fqn'>
{% block document_title %}Title Missing{% endblock %}
<span class='out-of-band'>
<span id='render-detail'>
<a id="collapse-all" href="#">[-]</a>
<a id="expand-all" href="#">[+]</a>
</span>
</span>
</h1>
{% block content %}<h2>Content missing.</h2>{% endblock %}
</section>
<section id='search' class="content hidden"></section>
<section class="footer"></section>
<div id="help" class="hidden">
<div class="shortcuts">
<h1>Keyboard shortcuts</h1>
<dl>
<dt>?</dt>
<dd>Show this help dialog</dd>
<dt>S</dt>
<dd>Focus the search field</dd>
<dt>&larrb;</dt>
<dd>Move up in search results</dd>
<dt>&rarrb;</dt>
<dd>Move down in search results</dd>
<dt>&#9166;</dt>
<dd>Go to active search result</dd>
</dl>
</div>
<div class="infos">
<h1>Search tricks</h1>
<p>
Prefix searches with a type followed by a colon (e.g.
<code>fn:</code>) to restrict the search to a given type.
</p>
<p>
Accepted types are: <code>fn</code>, <code>mod</code>,
<code>struct</code> (or <code>str</code>), <code>enum</code>,
<code>trait</code>, <code>typedef</code> (or
<code>tdef</code>).
</p>
</div>
</div>
<script>
window.rootPath = "{{ ('../' * level) if level }}";
</script>
<script src="{{ static('jquery.js') }}"></script>
<script src="{{ static('main.js') }}"></script>
<script async src="{{ root('search-index.js') }}"></script>
<center>Generated on <script src="{{ root('date.js') }}"></script></center>
<center>&copy;2016 - Eluna Lua Engine</center>
</body>
</html>

View File

@@ -0,0 +1,40 @@
{% extends '_base.html' %}
{% block title -%}
{{ current_class.name }} - {{ super() }}
{%- endblock %}
{% block description -%}
API documentation for the {{ current_class.name }} class in the Eluna engine.
{%- endblock %}
{% block document_title -%}
Class <a class="mod" href="">{{ current_class.name }}</a>
{%- endblock %}
{% block sidebar %}
{% endblock %}
{% block content %}
{{ current_class.description|parse_links }}
<h2 id='methods' class='section-header'><a href="#methods">Methods</a></h2>
<table>
{%- for method in current_class.methods %}
<tr>
<td>
<a class='stability {{ 'Stable' if method.documented else 'Experimental' }}' title='{{ 'Documented' if method.documented else 'Undocumented' }}'></a>
<a class='fn' href='{{ method.name }}.html'>{{ method.name }}</a>
</td>
<td class='docblock short'>
<p>{{ method.short_description|parse_links }}</p>
</td>
</tr>
{%- endfor %}
</table>
{% endblock %}

View File

@@ -0,0 +1 @@
document.write("{{ currdate }}");

View File

@@ -0,0 +1 @@
{% extends '_base.html' %}

View File

@@ -0,0 +1,77 @@
{% extends '_base.html' %}
{% block document_title -%}
Eluna API Documentation
{%- endblock %}
{% block content %}
<div class='docblock'>
<h1 id="the-eluna-engine-api" class='section-header'>
<a href="#the-eluna-engine-api">The Eluna Lua Engine&copy; API</a>
</h1>
<p>
The Eluna Lua Engine&copy; API allows you to add your own Lua code to be executed when certain events (called "hooks") occur.
</p>
<p>
Add a new in-game command, give life to creatures with new AI, or even light players who try to duel on fire!
If the hook exists, you can script it.
</p>
<h2 id="about-eluna" class='section-header'>
<a href="#about-eluna">About Eluna</a>
</h2>
<p>
Eluna is a <a href="http://www.lua.org/">Lua</a> engine for World of Warcraft emulators.
Eluna supports <a href="http://cmangos.net/">CMaNGOS</a>/<a href="https://www.getmangos.eu/home.php">MaNGOS</a>
and <a href="http://www.trinitycore.org/">TrinityCore</a>.
</p>
<p>
To get Eluna, simply clone your favorite version of MaNGOS or Trinity from
<a href="https://github.com/ElunaLuaEngine">our Github account</a>.
Each fork there has Eluna already integrated, so you just need to compile and go!
</p>
<p>
Follow us on <a href="https://twitter.com/EmuDevs">our Twitter page</a> to view the latest news about what's going on with Eluna.
</p>
<h2 id="tutorials-and-guides" class='section-header'>
<a href="#tutorials-and-guides">Tutorials & Guides</a>
</h2>
<p>
We haven't written tutorials yet, but when we do, we'll put the links here.
</p>
<h2 id="about-this-documentation" class='section-header'>
<a href="#about-this-documentation">About this documentation</a>
</h2>
<p>
The layout, CSS, and Javascript code for this documentation was borrowed from <a href="http://doc.rust-lang.org/">doc.rust-lang.org</a>.
</p>
<p>
The documentation generator was originally written by <a href="https://github.com/Patman64">Patman64</a> and is maintained by the Eluna team.
</p>
</div>
<h2 id='modules' class='section-header'><a href="#modules">Classes</a></h2>
<table>
{%- for class in classes %}
<tr>
<td>
{%- if class.fully_documented %}
<a class='stability Stable' title='Fully Documented'></a>
{%- elif class.fully_undocumented %}
<a class='stability Experimental' title='Fully Undocumented'></a>
{%- else %}
<a class='stability Unstable' title='Partially Documented'></a>
{%- endif %}
<a class='mod' href='{{ root(class.name + '/index.html') }}'>{{ class.name }}</a>
</td>
<td class='docblock short'>
<p>{{ class.short_description|parse_links }}</p>
</td>
</tr>
{%- endfor %}
</table>
{% endblock %}

View File

@@ -0,0 +1,95 @@
{% extends '_base.html' %}
{% block title -%}
{{ current_class.name }}:{{ current_method.name }} - Eluna
{%- endblock %}
{% block description -%}
API documentation for the {{ current_class.name }}:{{ current_method.name }} method in the Eluna engine.
{%- endblock %}
{% block document_title -%}
Method
<a class="mod" href="{{ root(current_class.name + '/index.html') }}">
{{- current_class.name -}}
</a>:<a class="fn" href="{{ root(current_class.name + '/' + current_method.name + '.html') }}">
{{- current_method.name -}}
</a>
{%- endblock %}
{% block sidebar %}
<h2>{{ current_class.name }} Methods</h2>
<script src="sidebar.js"></script>
<script>
// Highlight current method by adding "current" class to it
var element = document.getElementById("{{ current_class.name + ':' + current_method.name }}");
if (element) {
var classname = "current";
arr = element.className.split(" ");
if (arr.indexOf(classname) == -1) {
element.className += " " + classname;
}
}
</script>
{% endblock %}
{% block content %}
<div class='docblock'>
{%- if current_method.documented %}
{{ current_method.description|parse_links }}
{%- else %}
<p>This method is <em>undocumented</em>. <strong>Use at your own risk.</strong></p>
<p>For temporary documentation, please check the <a href="https://github.com/ElunaLuaEngine/Eluna/blob/master/LuaFunctions.cpp">LuaFunctions</a> source file.</p>
{%- endif %}
<h2 id="synopsis" class='section-header'>
<a href="#synopsis">Synopsis</a>
</h2>
{%- for prototype in current_method.prototypes %}
<p>
<code>{{ prototype }}</code>
</p>
{%- endfor %}
<h2 id="arguments" class='section-header'>
<a href="#arguments">Arguments</a>
</h2>
<p>
{%- if current_method.parameters|length > 0 %}
{%- for param in current_method.parameters %}
<dl>
<dt><code>{{ param.data_type|escape|parse_data_type }} {{ param.name if param.data_type != '...' }} {{- ' (' + param.default_value + ')' if param.default_value }}</code></dt>
<dd class="docblock">{{ param.description|parse_links if param.description else '<em>See method description.</em>' }}</dd>
</dl>
{%- endfor %}
{%- elif not current_method.documented %}
Unknown.
{%- else %}
None.
{%- endif %}
</p>
<h2 id="returns" class='section-header'>
<a href="#returns">Returns</a>
</h2>
<p>
{%- if current_method.returned|length > 0 %}
{%- for returned in current_method.returned %}
<dl>
<dt><code>{{ returned.data_type|escape|parse_data_type }} {{ returned.name }}</code></dt>
<dd class="docblock">{{ returned.description|parse_links if returned.description else '<em>See method description.</em>' }}</dd>
</dl>
{%- endfor %}
{%- elif not current_method.documented %}
Unknown.
{%- else %}
Nothing.
{%- endif %}
</p>
</div>
{% endblock %}

View File

@@ -0,0 +1,15 @@
var searchIndex = {};
{% for class in classes -%}
searchIndex["{{ class.name }}"] = {
"items": [
[0, "", "{{ class.name }}", "{{ class.short_description|replace('\n', ' ')|replace('\"', '&#34;')|parse_links|replace('"', '\\"') }}"],
{%- for method in class.methods %}
[3, "{{ method.name }}", "", "{{ method.short_description|replace('\n', ' ')|replace('\"', '&#34;')|parse_links|replace('"', '\\"') }}"],
{%- endfor %}
],
"paths": []
};
{%- endfor %}
initSearch(searchIndex);

View File

@@ -0,0 +1,5 @@
document.write(`
{%- for method in current_class.methods %}
<a id="{{ current_class.name + ':' + method.name }}" class="fn" href="{{ root(current_class.name + '/' + method.name + '.html') }}">{{ method.name }}</a>
{%- endfor %}
`);

View File

@@ -0,0 +1,102 @@
# Eluna features
This article contains information about features and important notes regarding Eluna.
## Settings
Eluna has some settings in the server configuration file.
It is important that you use the new configuration file that you get from compiling after adding Eluna. If the new configuration file is not used you will not receive any error log or output to console.
The configuration file includes at least the following settings:
- enable and disable Eluna
- enable and disable traceback function - this adds extra debug information if you have the default Eluna extensions.
- configure script folder location
- configure Eluna logging settings
## Reloading
To make testing easier it is good to know that Eluna scripts can be reloaded by using the command `.reload eluna`.
However this command should be used for development purposes __ONLY__. If you are having issues getting something working __restart__ the server.
It is important to know that reloading does not trigger for example the login hook for players that are already logged in when reloading.
## Script loading
Eluna loads scripts from the `lua_scripts` folder by default. You can configure the folder name and location in the server configuration file.
Any hidden folders are not loaded. All script files must have an unique name, otherwise an error is printed and only the first file found is loaded.
The loading order is not guaranteed to be alphabetic.
Any file having `.ext` extension, for example `test.ext`, is loaded before normal lua files.
Instead of the ext special feature however it is recommended to use the basic lua `require` function.
The whole script folder structure is added automatically to the lua require path so using require is as simple as providing the file name without any extension for example `require("runfirst")` to require the file `runfirst.lua`.
## Automatic conversion
In C++ level code you have types like `Unit` and `Creature` and `Player`.
When in code you have an object of type `Unit` you need to convert it to a `Creature` or a `Player` object to be able to access the methods of the subclass.
In Eluna this is automatic. All objects are automatically converted to the correct type and you will always have full access to all member functions of an object.
## Storing userdata
Storing userdata objects over time that are memory managed by C++ is a bad idea.
For example you should never save a player to a global variable and then try access it in a timed event. The reason is that the player object in C++ is a pointer to an object that C++ can delete at any time. When time passes the player may have logged out and using the pointer after player object no longer exists can be catastrophic.
To prevent users from doing this objects that are memory managed by C++ are automatically turned into nil when they are no longer safe to be accessed - this means usually after the hooked function ends.
Instead of storing the object itself you can use store guids `player:GetGUID()` and fetch the object by the guid with `map:GetWorldObject(guid)`.
Any userdata object that is memory managed by lua is safe to store over time. These objects include but are not limited to: query results, worldpackets, uint64 and int64 numbers.
## Userdata metamethods
All userdata objects in Eluna have tostring metamethod implemented.
This allows you to print the player object for example and to use `tostring(player)`.
The userdata uses metatables that contain the methods and functions it uses.
These tables are globally accessible by using the type name. For example `Player` is a global table containing all Player methods.
You can define new methods in lua for a class using these global tables.
```lua
function Player:CustomFunc(param1)
-- self is the player the method is used on
self:SendBroadcastMessage(param1)
end
function GameObject:CustomFunc(param1)
-- self is the gameobject the method is used on
print(self:GetName())
end
-- Example use:
player:CustomFunc("test")
gob:CustomFunc("test2")
```
It is recommended that in normal code these global tables and their names (variables starting with capital letters like Player, Creature, GameObject, Spell..) are avoided so they are not unintentionally edited or deleted causing other scripts possibly not to function.
## Database
Database is a great thing, but it has it's own issues.
### Querying
Database queries are slow. The whole server has to wait for the script to fetch the data from disk before continuing. Compared to reading cache or RAM reading from disk is the same as going to the moon to fetch the data (no pun intended).
Depending on what you need, prefer database Execute over Query when not selecting anything from the database. Database Executes are made asynchronously and they will not keep the server waiting.
Move all database queries possible to the script loading, server startup or similar one time event and use cache tables to manage the data in scripts.
### Types
__Database types should be followed strictly.__
Mysql does math in bigint and decimal formats which is why a simple select like `SELECT 1;` actually returns a bigint.
If you fetch a bigint or decimal using a function for a smaller type it is possible the value is read incorrectly.
For example the same code for fetching the result of `SELECT 1;` returned 1 on one machine and 0 on another. Using the correct function, in this case GetInt64, the right result was returned on both. https://github.com/ElunaLuaEngine/Eluna/issues/89#issuecomment-64121361
| base type | defined type | database type |
|---------------------------|--------------|-----------------------|
| char | int8 | tinyint(3) |
| short int | int16 | smallint(5) |
| (long int / int) | int32 | mediumint(8) |
| (long int / int) | int32 | int(10) |
| long long int | int64 | bigint(20) |
| unsigned char | uint8 | tinyint(3) unsigned |
| unsigned short int | uint16 | smallint(5) unsigned |
| unsigned (long int / int) | uint32 | mediumint(8) unsigned |
| unsigned (long int / int) | uint32 | int(10) unsigned |
| unsigned long long int | uint64 | bigint(20) unsigned |
| float | float | float |
| double | double | double and decimal |
| std::string | std::string | any text type |

View File

@@ -0,0 +1,42 @@
# Installing and updating
This page will help you get a cMaNGOS and a TrinityCore source with Eluna.
If you are looking to get MaNGOS source with Eluna head over to [MaNGOS forum](http://getmangos.eu/) for the installation and updating instructions - however read this page also as it contains important information.
If you are having trouble with the installation or updating the core source, head over to our [support forum](../README.md#documentation).
If you are looking for a way to merge eluna with a fork of the official repositories see [merging](MERGING.md).
### Requirements and dependencies:
**Eluna uses `C++11` so you need a compiler that supports it.**
**Eluna can use ACE or BOOST for filesystem library.**
Additionally see you desired core's documentation and installation instructions for it's requirements and dependencies.
### Installation
1. Open [git bash](http://git-scm.com/) and navigate to where you want the core source
2. Choose the git address of your desired core and patch below and clone the core with `git clone <address>`.
For example `git clone https://github.com/ElunaLuaEngine/ElunaTrinityWotlk.git`
* TrinityCore WoTLK: `https://github.com/ElunaLuaEngine/ElunaTrinityWotlk.git`
* cMaNGOS Classic: `https://github.com/ElunaLuaEngine/ElunaMangosClassic.git`
* cMaNGOS TBC: `https://github.com/ElunaLuaEngine/ElunaMangosTbc.git`
* cMaNGOS WoTLK: `https://github.com/ElunaLuaEngine/ElunaMangosWotlk.git`
3. Navigate to the newly created source folder with `git bash`
4. Use the git command `git submodule init` followed by `git submodule update`
* If you really do not get how to use git bash (and do try!) you can navigate to the `LuaEngine` folder and clone the [eluna repository](https://github.com/ElunaLuaEngine/Eluna) there. This is not recommended though.
4. Continue compiling the core normally using the official instructions
* [TrinityCore](http://collab.kpsn.org/display/tc/Installation+Guide)
* [cMaNGOS](https://github.com/cmangos/issues/wiki/Installation-Instructions)
__Important!__ After compiling use the new configuration files. They contain Eluna settings and without them Eluna may not function correctly. For example you do not get any error messages or error log.
After installing Eluna you should check out these:
- [Eluna getting started](USAGE.md)
- [Eluna features](IMPL_DETAILS.md)
### Updating
Updating is essentially handled in the same manner as you would normally update the core and database.
To get the newest core source code open `git bash` and navigate to your local source folder.
Then execute use `git pull` followed by `git submodule init` and `git submodule update`.
After updating the source you need to recompile the core normally. Simply use `CMake` if needed and compile.
To update the databases refer to the core's or database's official updating documents:
* [TrinityCore](http://collab.kpsn.org/display/tc/Databases+Installation)
* [cMaNGOS](https://github.com/cmangos/issues/wiki/Installation-Instructions)

View File

@@ -0,0 +1,42 @@
# Merging Eluna
Eluna can be added to various sources by applying the core changes required for Eluna to function.
Below you find the guides for merging Eluna with each core or a fork of it.
If you choose to merge you should be able to maintain and update yourself - we do not maintain your core. View Unofficial Merging below.
We also do not fix any merging errors you may have, but you are free to ask about them on the [support forum](../README.md#documentation) and we may assist.
We recommend using the [installation guide](INSTALL.md) especially if you are not familiar with git and updating the code.
It allows you to simply use `git pull` followed by `git submodule update` to update your source and we will handle the merging and maintenance with the official core source. Naturally you still need to handle updating the database as instructed by the core's wiki or instructions.
### Merging Eluna with MaNGOS
Eluna is merged with [official MaNGOS](http://getmangos.eu/) by default.
### Merging Eluna with cMaNGOS
```
git clone https://github.com/cmangos/mangos-wotlk.git
cd mangos-wotlk
git pull --recurse-submodules https://github.com/ElunaLuaEngine/ElunaMangosWotlk.git
```
Steps explained:
1. clone the core or fork source or get the it by other means
2. navigate to the source folder
3. pull the Eluna fork. This will fetch the repository and merge it with your source.
* `--recurse-submodules` will automatically pull the submodules (Eluna repository). You may need to use `git submodule init` followed by `git submodule update` if your Eluna folder is empty
* it is important that you choose the correct Eluna fork for your core andpatch:
* [Eluna cMaNGOS Classic](https://github.com/ElunaLuaEngine/ElunaMangosClassic)
* [Eluna cMaNGOS TBC](https://github.com/ElunaLuaEngine/ElunaMangosTbc)
* [Eluna cMaNGOS WotLK](https://github.com/ElunaLuaEngine/ElunaMangosWotlk)
### Merging Eluna with TrinityCore
```
git clone https://github.com/TrinityCore/TrinityCore.git -b3.3.5
cd TrinityCore
git pull --recurse-submodules https://github.com/ElunaLuaEngine/ElunaTrinityWotlk.git
```
Steps explained:
1. clone the core or fork source or get the it by other means
2. navigate to the source folder
3. pull the Eluna fork. This will fetch the repository and merge it with your source.
* `--recurse-submodules` will automatically pull the submodules (Eluna repository). You may need to use `git submodule init` followed by `git submodule update` if your Eluna folder is empty
* it is important that you choose the correct Eluna fork for your core and patch:
* [Eluna TrinityCore WotLK](https://github.com/ElunaLuaEngine/ElunaTrinityWotlk)
* [Eluna TrinityCore Cataclysm](https://github.com/ElunaLuaEngine/ElunaTrinityCata)

116
src/LuaEngine/docs/USAGE.md Normal file
View File

@@ -0,0 +1,116 @@
# Using Eluna
Eluna is a lua engine implementation for world of warcraft emulators.
It can be used to create different kind of scripts from AI to events.
This article helps you to get started with Eluna. We go through adding a simple script, where to get information from and a few language basics.
This article assumes you have already installed Eluna successfully. If you have not, see [installation](INSTALL.md).
## Basic script
Here is a simple "Hello world" example.
Create a file named `hello world.lua` that contains the following code and place the file inside the scripts folder in your server folder. By default the scripts folder is called `lua_scripts`. The server folder is the folder which contains server executables.
```lua
local PLAYER_EVENT_ON_LOGIN = 3
local function OnLogin(event, player)
player:SendBroadcastMessage("Hello world")
end
RegisterPlayerEvent(PLAYER_EVENT_ON_LOGIN, OnLogin)
```
If you now restart your server and log in game you are greeted with "Hello world" in your chat.
### What happened
As you have witnessed here no core compiling was needed and your script runs from the file you just created.
The file is compiled and run by the lua engine when the server starts up or Eluna is reloaded.
The code in the file registers a function to be run when a player logs in and the function sends a message to the player that logged in.
## Lua basics
It is good to get to know a bit of lua to code lua scripts. In this article we do not go that much into explaining lua syntax. Here are some pointers to important sources of information and things to get to know about.
### Sources of information
- lua users wiki
- http://lua-users.org/wiki/LuaDirectory
- http://lua-users.org/wiki/TutorialDirectory
- http://lua-users.org/wiki/SampleCode
- programming in lua http://www.lua.org/pil/1.html
- lua reference manual http://www.lua.org/manual/5.2/
### some highlights
- Print function outputs to server console. Very useful for simple debugging `print("anything here")`.
- control structures - especially loops:
- http://lua-users.org/wiki/ControlStructureTutorial
- http://www.lua.org/manual/5.2/manual.html#3.3.5
- lua string library:
- http://lua-users.org/wiki/StringLibraryTutorial
- http://www.wowwiki.com/Pattern_matching
- Lua tables are the only container in lua and they are essential for good code. Lua tables can be compared to arrays and hash maps.
Table functions and tutorials:
- http://www.lua.org/manual/5.2/manual.html#6.5
- http://www.lua.org/manual/5.2/manual.html#4.3
- http://lua-users.org/wiki/TablesTutorial
- http://www.lua.org/pil/2.5.html
- prefer local variables over global. While global variables may work they can create issues with other scripts that use same variable names.
All local variables outside of functions in a script are shared by everything running the same script - the variables are locally global.
## Eluna basics
It is good to know where you can find information about Eluna and Eluna's API as well as the basic elements of a script. Here are links to the main sources of information:
- Eluna features [Eluna details](IMPL_DETAILS.md)
- Eluna documentation http://elunaluaengine.github.io/
### Error messages
If Eluna is installed correctly, the default installation should make errors output to the console as well as a log file in the server folder. If you can not get your script to work, be sure to check the log file for any errors you might have missed.
Check out the configuration file for settings if you want to tweak the logging settings.
### Global functions
Global functions are functions you can run from anywhere in a script file and they do not require any object to be run.
In addition to normal global functions lua provides like `print` Eluna has it's own gobal functions. You can find them in the documentation under `global` class: [global functions](http://elunaluaengine.github.io/Global/index.html).
```lua
-- print the return value of GetLuaEngine function
print(GetLuaEngine())
```
### Member functions
Member functions, also called methods, are functions that require an userdata object to run. There are several different classes of objects that have different member functions. You can find all the member functions and their documentations from the [Eluna documentation](http://elunaluaengine.github.io/).
Classes in C++ inherit each other. In Eluna member functions are also inherited. For example objects of classes `Player` and `Creature` inherit all methods from `Unit` class.
Methods are called by using `:` notation on the object. For example to get the player name you can call the GetName methods like this: `player:GetName()`
```lua
local entry = 6
local on_combat = 1
local function OnCombat(event, creature, target)
-- creature is of type Creature
-- target is of type Creature or Player depending on who the creature is attacking
print(creature:GetLevel())
print(target:GetLevel())
end
RegisterCreatureEvent(entry, on_combat, OnCombat)
```
### Registering functions to events
Scripts register functions to events and the functions are executed when the event happens.
There are special global functions in Eluna API for registering functions for events.
You should be able to find all such functions from [Eluna documentation](http://elunaluaengine.github.io/) by searching `register`.
Functions used to register other functions for events need the ID of the event you want the hook to be registered for passed to them. You can find these ID numbers from the registering function documentation page.
Eluna passes some arguments to the functions executed. The arguments are always in same order. You can name them in any way you want. In the above script example the event `PLAYER_EVENT_ON_LOGIN` passes the event id and the player who logs in to the registered function. This is why the registered function has these parameters defined: `(event, player)`.
Some events allow the registered function to return different values. Sometimes you can return more than one value. The possibility to return is documented on the registering function's documentation page. Simply using the `return` keyword returns normally as if the function would end.
For example in this script we register the function `OnCombat` to be run on event `1`, which triggers on combat, for the creature entry `6`. All needed information can be found here: http://elunaluaengine.github.io/Global/RegisterCreatureEvent.html
```lua
local entry = 6
local on_combat = 1
local function OnCombat(event, creature, target)
creature:SendUnitYell("Yiee, me run!", 0)
end
RegisterCreatureEvent(entry, on_combat, OnCombat)
```