-
-
Notifications
You must be signed in to change notification settings - Fork 30
/
ObjectTag.java
258 lines (232 loc) · 10.7 KB
/
ObjectTag.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
package com.denizenscript.denizencore.objects;
import com.denizenscript.denizencore.events.ScriptEvent;
import com.denizenscript.denizencore.objects.core.ElementTag;
import com.denizenscript.denizencore.tags.Attribute;
import com.denizenscript.denizencore.tags.TagContext;
import com.denizenscript.denizencore.utilities.CoreUtilities;
public interface ObjectTag {
// <--[ObjectType]
// @name ObjectTag
// @prefix None
// @base None
// @format
// N/A
//
// @description
// "ObjectTag" is a pseudo-ObjectType that represents the fundamental root of all object types.
// If a tag says it returns an "ObjectTag", that means it can return any type of tag
// (for example MapTag.get[...] returns an object of ... well, whatever type the value happens to be, so it's simply documented as "ObjectTag").
//
// -->
/*
* ObjectTags should contain these two static methods, of which valueOf contains a valid
* annotation for ObjectFetcher
*
* public static ObjectTag valueOf(String string);
*
* valueOf() should take a string representation of the object, preferably with a valid object
* notation (x@), and turn it into a new instance of the ObjectTag. Care has to be taken to
* ensure that it is compatible with the tag system (ie. no periods (.) outside of square brackets),
* and other parts of Denizen.
*
* Since your object may be using the ObjectTag Attributes System, valueOf should take that into
* account as well.
*
*
* public static boolean matches()
*
* matches() should use some logic to determine if a string is in the proper format to possibly
* return a non-null valueOf() call.
*
*/
/**
* Retrieves the dScript argument prefix. ObjectTags should provide a default
* prefix if nothing else has been specified.
*
* @return the prefix
*/
String getPrefix();
/**
* Gets a debuggable format of the object. Like identify, but for console output.
*/
default String debuggable() {
return identify();
}
// <--[language]
// @name Unique Objects vs Generic Objects
// @group Object System
// @description
// There are a lot of object types in the Denizen object system, and not all of them behave the same way.
// It can be useful to separate object types into categories to better understand how objects work, and how Denizen as a whole works.
// While there are some hardlined separations, there are also some generalizations that don't necessarily hold exactly, but are still helpful.
// One such generalization is the separation between Unique and Generic object types.
//
// A UNIQUE object is the way you might assume all objects are.
// Unique objects most notably include EntityTag objects and the derivative NPCTag / PlayerTag objects.
// An entity object identifies in a form like 'e@<uuid>', where '<uuid>' is some unique ID that looks something like 'abc123-4d5e6f'.
// This ID is randomly generated by the Minecraft server and is used to identify that one entity in the world separately from any other.
// 'e@abc123' is not "a creeper" to the engine, it is "that specific creeper over there".
// An object that is unique must have some way to specify the exact single instance of it in the world, like the UUID used by entities.
//
// A GENERIC object can be said to be a 'description' of a unique object.
// A generic form of an EntityTag might look like 'e@creeper'.
// Instead of "that specific creeper over there", it is instead "the concept of a creeper mob".
// There is no way for the engine to read only the word 'creeper' and find out which specific creeper in the world it came from...
// it could be any of them, there's often hundreds of creepers spawned somewhere in the world at any time.
// Objects like items and materials are always generic. There is no unique identifier for any item, there is only the description.
// An item usually looks something like 'i@stick'.
// ItemTag objects can include more detail, like 'i@stick[lore=hi;display_name=My Stick;enchantments=[sharpness=5]]'...
// but this is still just a description, and there could still be many items out there that match this description.
//
// The consequences of this mostly relate to:
// - How you adjust an object (eg change the lore of an item, or teleport an entity).
// For example: you can't teleport the generic concept of a creeper, but you can certainly teleport a specific single creeper in the world.
// - How reliable tags on the object are.
// For example: the result of 'ItemTag.lore' on the item 'i@stick[lore=hi]' will always be 'hi' (because ItemTags are generic),
// but the result of 'EntityTag.location' on the entity 'e@abc123' will change every tick as the entity moves,
// or even become invalid if the entity dies (because that EntityTag is unique).
//
// Here's where the separation gets muddy:
// First, as mentioned, an EntityTag can either be unique ('e@abc123', 'n@42', etc.) OR generic ('e@creeper', 'e@zombie[custom_name=Bob]', etc).
// Second, some object types exhibit a bit of both.
//
// For example, a LocationTag refers to a unique block in the world (like, 'l@1,2,3,world' is always that specific block at that position),
// but also is a generic object in terms of the location object itself - if for example you want to change the angle of a location,
// you have to essentially 'create' a new location, that is an exact copy of the previous one but with a new yaw or pitch value.
// -->
/**
* Determines if this argument object is unique. This typically stipulates
* that this object has been named, or has some unique identifier that
* Denizen can use to recall it.
*
* @return true if this object is unique, false if it is a 'singleton generic argument/object'
*/
boolean isUnique();
/**
* Returns the string type of the object. This is fairly verbose and crude, but used with
* a basic dScriptArg attribute.
*
* @return a straight-up string description of the type of dScriptArg. ie. ListTag, LocationTag
*/
String getObjectType();
/**
* Gets an ugly, but exact, string representation of this ObjectTag.
* While not specified in the ObjectTag Interface, this value should be
* able to be used with a static valueOf(String) method to reconstruct the object.
*
* @return a single-line string representation of this argument
*/
String identify();
default String savable() {
return identify();
}
/**
* Gets an overall string representation of this ObjectTag.
* This should give the basic jist of the object being identified, but
* won't include the exactness that identify() uses.
* <p/>
* <code>
* Example: i@gold_sword vs. i@gold_sword[display_name=Shiny Sword]
* ^ ^
* +--- identifySimple() +--- identify()
* </code>
* <p/>
* This may produce the same results as identify(), depending on the complexity
* of the object being identified.
*
* @return a single-line, 'simple' string representation of this argument
*/
String identifySimple();
/**
* Create a duplicate of this object, if needed. Primarily for object types where this could specifically matter.
* Immutable ObjectTags do not need to implement.
*/
default ObjectTag duplicate() {
return this;
}
/**
* If any fixes need to be handled after properties are applied to an object, they should be handled here.
*/
default ObjectTag fixAfterProperties() {
return this;
}
/**
* Sets the prefix for this argument, otherwise uses the default.
*
* @return the ObjectTag
*/
ObjectTag setPrefix(String prefix);
default Class<? extends ObjectTag> getObjectTagClass() {
return getClass();
}
/**
* Converts the object to the given type. May error and/or return null if conversion is not possible.
*/
default <T extends ObjectTag> T asType(Class<T> type, TagContext context) {
return CoreUtilities.asType(this, type, context);
}
/**
* Returns whether the object is likely intended to be the type.
* This is if it already is that type, or if it has the proper prefixing to be that type.
*/
default boolean shouldBeType(Class<? extends ObjectTag> type) {
return CoreUtilities.shouldBeType(this, type);
}
/**
* Returns whether the object can probably be converted to the type.
* This is if it already is that type, or if it's reasonably convertible.
*/
default boolean canBeType(Class<? extends ObjectTag> type) {
return CoreUtilities.canPossiblyBeType(this, type);
}
default ElementTag asElement() {
return new ElementTag(toString());
}
/**
* Gets a specific attribute using this object to fetch the necessary data.
*
* @param attribute the name of the attribute
* @return a string result of the fetched attribute
*/
default ObjectTag getObjectAttribute(Attribute attribute) {
return null;
}
/**
* Get the "next object type down" - by default, an ElementTag of identify(), but can be different in some cases (eg a Player's next type down is Entity).
* Should never be null.
*/
default ObjectTag getNextObjectTypeDown() {
return new ElementTag(identify());
}
/**
* Optional special dynamic tag handling
*/
default ObjectTag specialTagProcessing(Attribute attribute) {
return null;
}
/**
* Optional "truthiness" boolean - most values are "true" except for nulls, zeros, errors, empty lists, etc.
*/
default boolean isTruthy() {
return true;
}
/**
* Used for objects to override, should not be called externally. Call 'tryAdvancedMatcher' instead.
*/
default boolean advancedMatches(String matcher) {
return ScriptEvent.runGenericCheck(matcher, identify());
}
/**
* Returns whether this object matches the specified input using 'advanced matcher' logic.
* Do not override.
*/
default boolean tryAdvancedMatcher(String matcher) {
if (matcher == null || matcher.isEmpty()) {
return false;
}
if (matcher.startsWith("!")) {
return !tryAdvancedMatcher(matcher.substring(1));
}
return advancedMatches(matcher);
}
}