Skip to content

Commit

Permalink
GameObjectHandler enhancement (#156)
Browse files Browse the repository at this point in the history
* tons of goh changes & other

* deprecate IDynamicGroovyProperty

* remove IDynamicGroovyProperty lsp support

* fix mod compat

* hide properties with alias name in completion

* handle goh conflicts

* fix crash when no element found

* break out into GroovyPropertyContainer

* some reviews

* more reviews

* goh -> ObjectMapper

* some reviews

* more reviews & fix merge

* IGameObjectParser -> IObjectParser & fix comments

* small fixes

* objectMapper() -> objectMapperBuilder()

* some java docs

* fix errors
  • Loading branch information
brachy84 authored Jun 16, 2024
1 parent 99f3c60 commit 6b5a659
Show file tree
Hide file tree
Showing 79 changed files with 1,988 additions and 1,488 deletions.
8 changes: 6 additions & 2 deletions src/main/java/com/cleanroommc/groovyscript/GroovyScript.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
import com.cleanroommc.groovyscript.documentation.Documentation;
import com.cleanroommc.groovyscript.documentation.linkgenerator.LinkGeneratorHooks;
import com.cleanroommc.groovyscript.event.EventHandler;
import com.cleanroommc.groovyscript.gameobjects.GameObjectHandlerManager;
import com.cleanroommc.groovyscript.helper.JsonHelper;
import com.cleanroommc.groovyscript.mapper.ObjectMapper;
import com.cleanroommc.groovyscript.mapper.ObjectMapperManager;
import com.cleanroommc.groovyscript.network.CReload;
import com.cleanroommc.groovyscript.network.NetworkHandler;
import com.cleanroommc.groovyscript.network.NetworkUtils;
Expand Down Expand Up @@ -155,9 +156,12 @@ public static void initializeRunConfig(File minecraftHome) {
@ApiStatus.Internal
public static void initializeGroovyPreInit() {
// called via mixin in between construction and fml pre init
GameObjectHandlerManager.init();
ObjectMapperManager.init();
VanillaModule.initializeBinding();
ModSupport.init();
for (ObjectMapper<?> goh : ObjectMapperManager.getObjectMappers()) {
getSandbox().registerBinding(goh);
}
if (FMLLaunchHandler.isDeobfuscatedEnvironment()) Documentation.generate();
runGroovyScriptsInLoader(LoadStage.PRE_INIT);
}
Expand Down
42 changes: 12 additions & 30 deletions src/main/java/com/cleanroommc/groovyscript/api/GroovyPlugin.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.cleanroommc.groovyscript.api;

import com.cleanroommc.groovyscript.compat.mods.GroovyPropertyContainer;
import com.cleanroommc.groovyscript.compat.mods.ModPropertyContainer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
Expand All @@ -18,6 +19,17 @@ public interface GroovyPlugin extends IGroovyContainer {
*/
@GroovyBlacklist
@ApiStatus.OverrideOnly
default @Nullable GroovyPropertyContainer createGroovyPropertyContainer() {
return createModPropertyContainer();
}

/**
* @deprecated use {@link #createGroovyPropertyContainer()} instead
*/
@GroovyBlacklist
@ApiStatus.OverrideOnly
@Deprecated
@ApiStatus.ScheduledForRemoval(inVersion = "1.2.0")
default @Nullable ModPropertyContainer createModPropertyContainer() {
return null;
}
Expand All @@ -30,34 +42,4 @@ public interface GroovyPlugin extends IGroovyContainer {
default boolean isLoaded() {
return true;
}

/**
* Returns the override priority. Defines how this plugin should behave when another container with the same mod id exists.
* The return value should be as low as possible. Internal container always return {@link Priority#NONE}.
* @return the override priority
* @see Priority
*/
@NotNull
default Priority getOverridePriority() {
return Priority.NONE;
}

enum Priority {
/**
* Default. Can be overridden by anything and can't override anything.
*/
NONE,
/**
* Can override containers with priority NONE.
*/
OVERRIDE,
/**
* Can override containers with priority NONE, OVERRIDE.
*/
OVERRIDE_HIGH,
/**
* Can override containers with priority NONE, OVERRIDE, OVERRIDE_HIGH.
*/
OVERRIDE_HIGHEST
}
}
6 changes: 6 additions & 0 deletions src/main/java/com/cleanroommc/groovyscript/api/Hidden.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.cleanroommc.groovyscript.api;

public interface Hidden {

boolean isHidden();
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.cleanroommc.groovyscript.api;

import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

import java.util.Map;

/**
* When this is implemented on a class, {@link #getProperty(String)} will be called when groovy tries to get a field from this class
*/
@ApiStatus.ScheduledForRemoval(inVersion = "1.2.0")
@Deprecated
public interface IDynamicGroovyProperty {

/**
Expand Down Expand Up @@ -34,5 +37,5 @@ default boolean setProperty(String name, @Nullable Object value) {
*
* @return all properties
*/
Map<String, Object> getProperties();
Map<String, ?> getProperties();
}
Original file line number Diff line number Diff line change
@@ -1,83 +1,41 @@
package com.cleanroommc.groovyscript.api;

import com.cleanroommc.groovyscript.gameobjects.GameObjectHandlers;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

import java.util.Locale;
import java.util.Map;
import java.util.function.Function;

/**
* A bracket handler returns a object based on its input arguments.
* A bracket handler can be called from groovy lik this:
* <p>
* {@code bracket_handler_name(args)}
* </p>
* In the first way there is always only one argument which is a String.
* In the second method the argument size is at least, but not limited to one.
* The first argument is always a string. The other can be anything.
* @deprecated use {@link IObjectParser}
*/
@Deprecated
@ApiStatus.ScheduledForRemoval(inVersion = "1.2.0")
@FunctionalInterface
public interface IGameObjectParser<T> {
public interface IGameObjectParser<T> extends IObjectParser<T> {

/**
* Parses a object based on input arguments
*
* @param args arguments. length >= 1 && args[0] instanceof String
* @return a parsed Object
*/
@NotNull
Result<T> parse(String mainArg, Object[] args);

static <T extends IForgeRegistryEntry<T>> IGameObjectParser<T> wrapForgeRegistry(IForgeRegistry<T> forgeRegistry) {
return (s, args) -> {
Result<ResourceLocation> rl = GameObjectHandlers.parseResourceLocation(s, args);
if (rl.hasError()) return Result.error(rl.getError());
T value = forgeRegistry.getValue(rl.getValue());
return value == null ? Result.error() : Result.some(value);
};
static <T extends IForgeRegistryEntry<T>> IObjectParser<T> wrapForgeRegistry(IForgeRegistry<T> forgeRegistry) {
return IObjectParser.wrapForgeRegistry(forgeRegistry);
}

static <T extends Enum<T>> IGameObjectParser<T> wrapEnum(Class<T> enumClass, boolean caseSensitive) {
Map<String, T> map = new Object2ObjectOpenHashMap<>();
for (T t : enumClass.getEnumConstants()) {
map.put(caseSensitive ? t.name() : t.name().toUpperCase(Locale.ROOT), t);
}
return (s, args) -> {
T t = map.get(caseSensitive ? s : s.toUpperCase(Locale.ROOT));
return t == null ? Result.error() : Result.some(t);
};
static <T extends Enum<T>> IObjectParser<T> wrapEnum(Class<T> enumClass, boolean caseSensitive) {
return IObjectParser.wrapEnum(enumClass, caseSensitive);
}

static <T> IGameObjectParser<T> wrapStringGetter(Function<String, T> getter) {
return wrapStringGetter(getter, false);
static <T> IObjectParser<T> wrapStringGetter(Function<String, T> getter) {
return IObjectParser.wrapStringGetter(getter);
}

static <T> IGameObjectParser<T> wrapStringGetter(Function<String, T> getter, boolean isUpperCase) {
return (s, args) -> {
if (args.length > 0) {
return Result.error("extra arguments are not allowed");
}
T t = getter.apply(isUpperCase ? s.toUpperCase(Locale.ROOT) : s);
return t == null ? Result.error() : Result.some(t);
};
static <T> IObjectParser<T> wrapStringGetter(Function<String, T> getter, boolean isUpperCase) {
return IObjectParser.wrapStringGetter(getter, isUpperCase);
}

static <T, V> IGameObjectParser<T> wrapStringGetter(Function<String, V> getter, Function<V, @NotNull T> trueTypeFunction) {
return wrapStringGetter(getter, trueTypeFunction, false);
static <T, V> IObjectParser<T> wrapStringGetter(Function<String, V> getter, Function<V, @NotNull T> trueTypeFunction) {
return IObjectParser.wrapStringGetter(getter, trueTypeFunction);
}

static <T, V> IGameObjectParser<T> wrapStringGetter(Function<String, V> getter, Function<V, @NotNull T> trueTypeFunction, boolean isUpperCase) {
return (s, args) -> {
if (args.length > 0) {
return Result.error("extra arguments are not allowed");
}
V v = getter.apply(isUpperCase ? s.toUpperCase(Locale.ROOT) : s);
return v == null ? Result.error() : Result.some(trueTypeFunction.apply(v));
};
static <T, V> IObjectParser<T> wrapStringGetter(Function<String, V> getter, Function<V, @NotNull T> trueTypeFunction, boolean isUpperCase) {
return IObjectParser.wrapStringGetter(getter, trueTypeFunction, isUpperCase);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.cleanroommc.groovyscript.api;

import com.cleanroommc.groovyscript.compat.mods.GroovyContainer;
import com.cleanroommc.groovyscript.compat.mods.ModPropertyContainer;
import com.cleanroommc.groovyscript.compat.mods.GroovyPropertyContainer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;

Expand All @@ -17,7 +17,7 @@ public interface IGroovyContainer {

/**
* Returns the mod id of the compat mod. This will be used to check if the mod is loaded.
* Scripts will be able to refer to the mods {@link com.cleanroommc.groovyscript.compat.mods.ModPropertyContainer ModPropertyContainer}
* Scripts will be able to refer to the mods {@link com.cleanroommc.groovyscript.compat.mods.GroovyPropertyContainer GroovyPropertyContainer}
* with this id.
*
* @return the compat mod id
Expand All @@ -27,6 +27,7 @@ public interface IGroovyContainer {

/**
* Returns the name of this container. Is only used for logging and debugging.
* It usually returns a mod name, but it doesn't have to.
*
* @return the name of the container
*/
Expand All @@ -50,12 +51,43 @@ default Collection<String> getAliases() {
}

/**
* Called before scripts are executed for the first time. Called right before {@link ModPropertyContainer#initialize()}.
* Called before scripts are executed for the first time. Called right before {@link GroovyPropertyContainer#initialize(GroovyContainer)}.
* Used to initialize things like expansions with {@link com.cleanroommc.groovyscript.sandbox.expand.ExpansionHelper ExpansionHelper} and
* game object handlers with {@link com.cleanroommc.groovyscript.gameobjects.GameObjectHandlerManager GameObjectHandlerManager}.
* object mappers with {@link com.cleanroommc.groovyscript.mapper.ObjectMapperManager ObjectMapperManager}.
*
* @param container the created container for the compat mod
*/
@ApiStatus.OverrideOnly
void onCompatLoaded(GroovyContainer<?> container);

/**
* Returns the override priority. Defines how this plugin should behave when another container with the same mod id exists.
* The return value should be as low as possible. Internal container always return {@link Priority#NONE}.
*
* @return the override priority
* @see Priority
*/
@NotNull
default Priority getOverridePriority() {
return Priority.NONE;
}

enum Priority {
/**
* Default. Can be overridden by anything and can't override anything.
*/
NONE,
/**
* Can override containers with priority NONE.
*/
OVERRIDE,
/**
* Can override containers with priority NONE, OVERRIDE.
*/
OVERRIDE_HIGH,
/**
* Can override containers with priority NONE, OVERRIDE, OVERRIDE_HIGH.
*/
OVERRIDE_HIGHEST
}
}
80 changes: 80 additions & 0 deletions src/main/java/com/cleanroommc/groovyscript/api/IObjectParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.cleanroommc.groovyscript.api;

import com.cleanroommc.groovyscript.mapper.ObjectMappers;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
import org.jetbrains.annotations.NotNull;

import java.util.Locale;
import java.util.Map;
import java.util.function.Function;

/**
* A function to parse an object from a string and any amount of additional arguments of any type.
* This is used for {@link com.cleanroommc.groovyscript.mapper.ObjectMapper object mappers}.
*
* @param <T> type of the parsed objects
*/
@FunctionalInterface
public interface IObjectParser<T> {

/**
* Parses an object based on input arguments
*
* @param mainArg the first argument. Usually an id to get an object from a map.
* @param args additional arguments, like metadata for item stacks
* @return a parsed object
*/
@NotNull
Result<T> parse(String mainArg, Object[] args);

static <T extends IForgeRegistryEntry<T>> IObjectParser<T> wrapForgeRegistry(IForgeRegistry<T> forgeRegistry) {
return (s, args) -> {
Result<ResourceLocation> rl = ObjectMappers.parseResourceLocation(s, args);
if (rl.hasError()) return Result.error(rl.getError());
T value = forgeRegistry.getValue(rl.getValue());
return value == null ? Result.error() : Result.some(value);
};
}

static <T extends Enum<T>> IObjectParser<T> wrapEnum(Class<T> enumClass, boolean caseSensitive) {
Map<String, T> map = new Object2ObjectOpenHashMap<>();
for (T t : enumClass.getEnumConstants()) {
map.put(caseSensitive ? t.name() : t.name().toUpperCase(Locale.ROOT), t);
}
return (s, args) -> {
T t = map.get(caseSensitive ? s : s.toUpperCase(Locale.ROOT));
return t == null ? Result.error() : Result.some(t);
};
}

static <T> IObjectParser<T> wrapStringGetter(Function<String, T> getter) {
return wrapStringGetter(getter, false);
}

static <T> IObjectParser<T> wrapStringGetter(Function<String, T> getter, boolean isUpperCase) {
return (s, args) -> {
if (args.length > 0) {
return Result.error("extra arguments are not allowed");
}
T t = getter.apply(isUpperCase ? s.toUpperCase(Locale.ROOT) : s);
return t == null ? Result.error() : Result.some(t);
};
}

static <T, V> IObjectParser<T> wrapStringGetter(Function<String, V> getter, Function<V, @NotNull T> trueTypeFunction) {
return wrapStringGetter(getter, trueTypeFunction, false);
}

static <T, V> IObjectParser<T> wrapStringGetter(Function<String, V> getter, Function<V, @NotNull T> trueTypeFunction, boolean isUpperCase) {
return (s, args) -> {
if (args.length > 0) {
return Result.error("extra arguments are not allowed");
}
V v = getter.apply(isUpperCase ? s.toUpperCase(Locale.ROOT) : s);
return v == null ? Result.error() : Result.some(trueTypeFunction.apply(v));
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
import java.lang.reflect.Modifier;

/**
* A helper interface to register {@link INamed registries} without having direct access to the
* {@link com.cleanroommc.groovyscript.compat.mods.ModPropertyContainer ModPropertyContainer}.
* An instance can be obtained from {@link GroovyContainer#getVirtualizedRegistrar()}.
* @deprecated The methods of this class have been added directly to {@link GroovyContainer}
*/
@ApiStatus.ScheduledForRemoval(inVersion = "1.2.0")
@Deprecated
@ApiStatus.NonExtendable
public interface IRegistrar {

Expand All @@ -19,6 +19,7 @@ public interface IRegistrar {
*
* @param registry registry to add.
*/
@Deprecated
void addRegistry(INamed registry);

/**
Expand All @@ -27,6 +28,7 @@ public interface IRegistrar {
*
* @param object object to add fields from
*/
@Deprecated
default void addFieldsOf(Object object) {
boolean staticOnly = false;
Class<?> clazz;
Expand Down
Loading

0 comments on commit 6b5a659

Please sign in to comment.