Conversation
Co-authored-by: Patrick Miller <apickledwalrus@gmail.com>
Co-authored-by: Patrick Miller <apickledwalrus@gmail.com>
Allows converting SyntaxInfos back into Builders
Fixes current test failures
|
Should there be the possibility of modifying already existing elements? Some expressions like the "name" expression are very generic and being able to add to them as an addon developer would be awesome, some use cases would be skbee merging its merge component expression with skript's join string expression, adding xyz coordinates of non standard objects to the coordinate expression; this could open up a world of fixes for conflicts and it would be very cool to see it implemented, and if it is i will surely use it. This would in a sense act like "patches". |
The ability to unregister, modify, and then re-register infos exists. What you want is covered in #6728 |
* First api design * Rework and implement for expression * Implement for all syntax elements * Rename to Skript and other small refactors * Registration closing * Child key * Cleanup * Moving * hashcode * Oops * TODO * Move event pattern transformation to the correct place * Moves key implementation away * Apply suggestions from code review Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> * Apply suggestions from code review Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> * Requested changes * Package rename * Refactoring * Deprecation * Builders * Use builders * Certified license header moment * Not too proud of this one * Fix tests * Refactoring * Replace with shorter version * Fix order * Refactoring * Cherry-pick docs-tool into api-rework * Fix documentation * Double tabbing * Fix registrations * Attempt 2 * Apply suggestions from code review Co-authored-by: Patrick Miller <apickledwalrus@gmail.com> Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> * Allows updating of Skript instance. * Fix error * Deprecation * Simpler keys * SimpleSkriptRegistry * Priority stuff * Allow for multiple Skript instances * Suppliers for syntax elements * Fixes for event parsing changes * Improve BukkitOrigin; Remove LegacyEventImpl * Origin improvements * Rework SyntaxInfo/Builder system * Improve Registry and Key system * Improve SyntaxRegister docs * Improve SyntaxInfo implementations * Add missing JavaDocs * Require builders for creating SyntaxInfos * Initial implementation of new Addon API * Address UnderscoreTud's review * Address Moderocky's review * Improve compatibility implementation * Remove Skript State system * Minor Tweaks * Localizer system draft * Localizer Fixes * Add support for property registration methods * Add support for unregistering syntax infos * First pass at SkriptAddon rework This will not build. This is a design pivot to have SkriptAddon instances returned by registering an addon by name rather than implementing the interface. * Implement child registries for addons * Fix language and addon compatibility * Fix building and tests * Block registration of interfaces/abstract classes They are permitted if a creation supplier is provided * Origin reworks Added a default SkriptAddon origin and pivoted internally to avoid using an "unknown" origin * First pass at priority rework * Add priority support to info builders * Revert Structure priority changes * Pivot to a relational priority system * Replace ExpressionType with priority system * Revert some unnecessary changes * Add missing EventValueExpression registration api * Rework Expression priorities to be for all SyntaxInfos * Change Skript.createInstance to also return modifiable addon * Add SyntaxPriority An implementation of Priority that enables positioning itself around specific SyntaxElement classes. * Move module loading out of registration * Add missing PriorityImpl checks * SyntaxPriority improvements and clarifications * Limitations on SyntaxPriority * Alternative module loading method * Remove unnecessary SyntaxRegister interface * Fix simple structure support * Implement Registry interface * Remove SyntaxPriority Implementation It is too unstable in its current state. It may return in a future PR. * Disconnect Event Info from Structure Info For registration purposes, SkriptEvents are no longer tied to Structures (see StructEvent) * Allow structures to be simple or section * Improve SyntaxInfo parameter checks * Add class loading utilities * Replace ClassLoader Java 11 methods * SkriptAddon: remove unnecessary annotations * ClassLoader: add default loadClasses method * Use builder method for Expression return type * Remove license headers * Prevent SkriptImpl from exposing its addons * Use the same registry across all addons Addon-specific registries may return but will need reworked * Rename SkriptAddon#registry to SkriptAddon#syntaxRegistry * Add ViewProvider interface * Improve annotation usage and placement * Remove deprecation annotations We plan to merge this as experimental/preview API * Add listening behavior to Event SyntaxInfo Forgot about this... * Add missing experimental annotations * Rename BukkitInfos to BukkitSyntaxInfos * Add unmodifiableView for Skript With this change, I have also removed the weird NonNullPair from creating a default Skript. * Add registry storage to SkriptAddon * Add source requirement to SkriptAddon * Remove source from Localizer This can be obtained from the addon instance * Fix old addon registration * SyntaxInfo equals/hashCode improvements * Implementation optimizations * Fix ConditionType support * Return the constructed info for static registration methods * Builder interface * Remove unncessary package infos * Implement Buildable interface Allows converting SyntaxInfos back into Builders * Improve legacy collection methods * Use a map for modern addon tracking * Fix SkriptEventInfo Compatibility * Allow Skript#getAddon to return Skript's addon instance Fixes current test failures * Tweak Preconditions check * ExpressionInfo Builder: require returnType at creation * Add 'clear' methods to builders * AddonModule: add init phase * Fix incorrect Expression Builder uses --------- Co-authored-by: kiip1 <25848425+kiip1@users.noreply.github.com> Co-authored-by: LimeGlass <16087552+TheLimeGlass@users.noreply.github.com> Co-authored-by: Moderocky <admin@moderocky.com> Co-authored-by: sovdee <10354869+sovdeeth@users.noreply.github.com> Co-authored-by: Efnilite <35348263+Efnilite@users.noreply.github.com>
Description
This is built from the work done in #5331 (thanks @kiip1)
This PR is the initial effort to build a modern API for Skript. All new API has been marked as experimental. Backwards compatibility has been kept. The focus of this PR is to introduce a new addon registration process and syntax registration process. It also implements many new utility interfaces and classes for usage across Skript.
Breaking Changes
New Skript Core
The new Skript interface represents the core of the language. At this time, implementations require methods for registering an addon and obtaining a collection of all addons. The Skript interface is an extension of the SkriptAddon interface, which is further described below.
A default implementation is available, which is what the plugin uses. Below is an example of creating a Skript instance using the default implementation.
NOTE: The class parameter represents the main class of the application registering the addon.
New Addon API
The SkriptAddon interface is the center of the new Addon API. At this time, every SkriptAddon has a name, syntax registry, and localizer (the last two are described in further detail below)
An addon can be registered as follows:
Addon Modules
Also included in this new API are AddonModules, which enables Skript and its addons to be broken up into categories. For example, a Skript implementation not dependent on Minecraft might have a default module containing something like arithmetic.
Modules have two loading phases:
initandload.initis intended for registering components that might be used by other modules (e.g. class infos).loadis intended for registering components that are more specific to the module, such as syntax.When loading multiple modules together, their
initphases will first all be executed, and then theirloadphases will all be executed.Loading a module is rather simple:
New Syntax Registration API
The syntax registration API is the biggest component of this PR. The registration process has been completely overhauled to provide greater control over the registration process.
Syntax Infos
The SyntaxInfo interface is a replacement for the SyntaxElementInfo class.
Every SyntaxInfo has the following properties:
Two default extensions exist:
Additionally, one Bukkit-specific implementation exists for Bukkit events:
Builders
Four builders exist for creating a SyntaxInfo, SyntaxInfo.Expression, SyntaxInfo.Structure, and BukkitInfos.Event. These builders are implemented using the new Builder/Buildable interfaces, described below.
The following is an example of building a SyntaxInfo for LitPi with the builder for SyntaxInfo.Expression:
SyntaxOrigin
A SyntaxOrigin describes where a syntax has come from. By default, it only has a name (String) which describes the origin. When specified, this would likely be the name of an addon (see AddonOrigin). When not specified, it is the fully qualified name of the SyntaxElement class.
Priorities
The priority system is an evolution of ExpressionType. Unlike ExpressionType, which is only for expressions, the priority system applies to all SyntaxInfos. By default, Skript has three priorities:
ExpressionType#EVENTandExpressionType#PROPERTYhave been replaced with the constantsEventValueExpression#DEFAULT_PRIORITYandPropertyExpression#DEFAULT_PRIORITYrespectively. The former lies between SIMPLE/COMBINED while the latter lies between COMBINED/PATTERN_MATCHES_EVERYTHINGAdditionally,
PropertyCondition#DEFAULT_PRIORITYhas been added for conditions. It also lies between COMBINED/PATTERN_MATCHES_EVERYTHING.Unlike Structure priorities, this new Priority system is purely relational. There are no "magic" numbers that determine position. The definition for the default three may be useful for visualizing this:
Additionally, a Priority may be created that comes before some other Priority. If one wanted to create a Priority for syntax that should be parsed really early, they might do something like:
SyntaxRegistry
The SyntaxRegistry is the home for an addon's syntax infos. A SyntaxRegistry has three important methods:
syntaxes(Key)which returns all SyntaxInfos registered under a Key (e.g. all expressions, effects, etc.)register(Key, SyntaxInfo)which registers a SyntaxInfo under a Keyunregister(Key, SyntaxInfo)which unregisters a SyntaxInfo that is stored under a KeyA default implementation of a SyntaxRegistry may be obtained through:
Keys
Keys are used for storing SyntaxInfos of a certain type. Skript has six built in keys:
STRUCTUREfor structuresSECTIONfor sectionsSTATEMENTfor effects and conditions (Statement classes)EFFECTfor effectsCONDITIONfor conditionsEXPRESSIONfor expressionsCreating a Key constant is simple:
Now, if we wanted to access the Statements in a registry:
Child Keys
Child Keys are the same as Keys, but they also have a parent Key. Thus, when a SyntaxInfo is registered under a Child Key, it is also registered under the parent Key. This is how the register for Statements works, as the Effect and Condition Keys are Child Keys of the Statement key. We can use this example to see how to build Child Keys:
Experimental Localization API
This PR includes an experimental (and likely subject to change) API for Localization. At this point in time, it exists for modern addons to register their language files. I avoided creating too much API as that will be for a separate PR reworking localization. The key idea here is loading language files, which can be done as follows:
setSourceDirectoriestakes in two parameters:languageFileDirectorywhich is the path to the language file directory on the jar.dataFileDirectory(optional) which is the path to the language file directory on the disk (e.g. user customizable lang files).New Utilities
Builder/Buildable Interfaces
I have introduced two new interfaces for building objects along with converting objects into builders. The primary interface, builder, has two methods:
build()which is the terminal operation for a builder that returns an instance of the type being built.applyTo(Builder)which enables applying (copying) the values of one builder onto another.Another interface, Buildable, enables converting an object back into a builder. It has one method:
builder()which returns a builder representing the object.For example, one might want to add a new pattern to a SyntaxInfo. This is now trivial:
ClassLoader API
I have introduced a ClassLoader utility class which acts as a replacement for the
SkriptAddon#loadClassesmethod. It takes inspiration from the changes I had made in #4573.A simple utility method functioning the same as
SkriptAddon#loadClassesexists too:However, a builder exists for creating custom ClassLoaders with different behavior:
It is possible to load classes without passing
getFile()(or a jar), though it is not preferred due to reliability reasons. It worked fine during my testing, but the documentation for the utility used (ClassPath from Guava) notes some potential issues.ViewProvider Interface
An interface has been added to represent objects that can have unmodifiable views of themselves created. An unmodifiable view allows read access into an object but prevents making any changes to its values. For example, an unmodifiable SkriptAddon would allow you to obtain properties such as its name, but it would prevent changes such as storing a new registry or loading a new module.
Target Minecraft Versions: any
Requirements: none
Related Issues: