-
Notifications
You must be signed in to change notification settings - Fork 12
Services Overview
Anvil comes with several built-in services. The usage of a specific service can be found in the appropriately named section (under Services). Services are provided by guice. Use the following syntax for injection:
public class CommonFoo {
@Inject
KickService kickService;
...
}
Some services are generically typed. Classes that inject these must, therefore, provide the type. This can be done by declaring the type parameters in the class signature (if the class is in common) or by directly providing the concrete type (if the class is in a platform module)
In the common module:
public class CommonFoo<TString, TCommandSource> {
@Inject
StringResult<TString, TCommandSource> stringResult;
...
}
In a platform module:
public class SpongeFoo {
@Inject
StringResult<Text, CommandSource> stringResult;
...
}
There may be cases where conversion between objects of a generic type is necessary. In this example, we'll try to convert a TPlayer
to a TCommandSource
.
public class CommonFoo<
TUser,
TPlayer extends TCommandSource,
TString,
TCommandSource> {
@Inject
StringResult<TString, TCommandSource> stringResult;
@Inject
UserService<TUser, TPlayer> userService;
public void bar(UUID userUUID) {
TPlayer player = userService.getPlayer(userUUID)
.orElseThrow(IllegalStateException::new);
stringResult.builder()
.green().append("Hello!")
.sendTo(player);
}
}
The above code will work and is the ideal solution to the problem of converting between objects of generic types. However, there is a more complicated case where this will not work. In the following example, our TPlayer
needs to be bounded by both TCommandSource
but also TSubject
. This does not work in java because while it is possible to write:
TPlayer extends TCommandSource
It is not possible to write:
TPlayer extends TCommandSource & TSubject
Instead, we recommend deferring the type checking of generic types to runtime, by casting between types instead of writing bounds in the class signature. While it is an imperfect solution, we have to suck it up until java allows generic intersection bounds.
This is illustrated in the following example:
public class CommonFoo<
TPlayer, // extends TCommandSource & TSubject doesnt work
TUser,
TSubject,
TString,
TCommandSource> {
@Inject
StringResult<TString, TCommandSource> stringResult;
@Inject
UserService<TUser, TPlayer> userService;
@Inject
PermissionService<TSubject> permissionService;
@Override
public void foo(UUID userUUID) {
TPlayer player = userService.getPlayer(userUUID).orElseThrow(IllegalStateException::new);
// casting to TSubject
if (permissionService.hasPermission((TSubject) player, "somePerm")) {
// casting to TCommandSource
stringResult.builder().blue().append("Hello").sendTo((TCommandSource) player);
} else {
// casting to TCommandSource
stringResult.builder().red().append("Error").sendTo((TCommandSource) player);
}
}
}