This commit is contained in:
kento2 2026-02-14 20:19:23 +01:00
parent 07fa0efab7
commit 145a3116bc
29 changed files with 721 additions and 62 deletions

View file

@ -0,0 +1,29 @@
package de.kentoj.scrow;
import com.mongodb.reactivestreams.client.MongoDatabase;
import de.kentoj.scrow.services.EconomyService;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ScrowAPI {
@Getter
private static @Nullable MongoDatabase database;
@Getter
private static EconomyService economyService;
@ApiStatus.Internal
public static void setDatabase(@Nullable MongoDatabase database) {
ScrowAPI.database = database;
}
@ApiStatus.Internal
public static void setEconomyService(EconomyService economyService) {
ScrowAPI.economyService = economyService;
}
}

View file

@ -0,0 +1,7 @@
package de.kentoj.scrow.provider;
public class MissingServiceFieldException extends RuntimeException {
public MissingServiceFieldException(Class<?> apiClass, Class<?> serviceClass) {
super("missing static field of type " + serviceClass + " annotated with " + ServiceField.class.getCanonicalName() + " in " + apiClass.getCanonicalName());
}
}

View file

@ -0,0 +1,15 @@
package de.kentoj.scrow.provider;
import de.kentoj.scrow.provider.provider.ServiceProvider;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Set;
@Getter
@AllArgsConstructor
public class RegisteredService<T> {
private final Class<T> serviceClass;
private final ServiceProvider<T> serviceProvider;
private final Set<Class<?>> requiredServices;
}

View file

@ -0,0 +1,10 @@
package de.kentoj.scrow.provider;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceField {}

View file

@ -0,0 +1,68 @@
package de.kentoj.scrow.provider;
import de.kentoj.scrow.provider.provider.ServiceProvider;
import lombok.RequiredArgsConstructor;
import java.lang.reflect.AccessFlag;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@SuppressWarnings("unchecked")
@RequiredArgsConstructor
public class ServiceRegistry {
private final Set<RegisteredService<?>> registeredServices = new HashSet<>();
public <T> void provideService(
Class<T> serviceInterfaceClass,
ServiceProvider<T> serviceProvider,
Set<Class<?>> requiredServices
) {
registeredServices.add(new RegisteredService<>(serviceInterfaceClass, serviceProvider, requiredServices));
}
public void load(Class<?> apiClass) {
Arrays.stream(apiClass.getDeclaredFields())
.filter(f -> f.accessFlags().contains(AccessFlag.STATIC))
.filter(f -> f.isAnnotationPresent(ServiceField.class))
.filter(f -> {
try {
f.setAccessible(true);
return f.get(null) == null;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
})
.forEach(field -> loadServiceRecursively(apiClass, getService(field.getType())));
}
public void loadServiceRecursively(Class<?> apiClass, RegisteredService<?> registeredService) {
for (Class<?> serviceClass : registeredService.getRequiredServices()) {
var sv = getService(serviceClass);
loadServiceRecursively(apiClass, sv);
}
var field = Arrays.stream(apiClass.getDeclaredFields())
.filter(f -> f.accessFlags().contains(AccessFlag.STATIC))
.filter(f -> f.isAnnotationPresent(ServiceField.class))
.findFirst()
.orElseThrow(() -> new MissingServiceFieldException(apiClass, registeredService.getServiceClass()));
try {
if (field.get(null) != null) return;
field.setAccessible(true);
field.set(null, registeredService.getServiceProvider().get());
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public <T> RegisteredService<T> getService(Class<T> serviceClass) {
return (RegisteredService<T>) registeredServices.stream()
.filter(Objects::nonNull)
.filter(sv -> sv.getServiceClass() == serviceClass)
.findFirst()
.orElseThrow(() -> new IllegalStateException("No registered-service for " + serviceClass.getCanonicalName() + " registered"));
}
}

View file

@ -0,0 +1,15 @@
package de.kentoj.scrow.provider.provider;
import org.jetbrains.annotations.Nullable;
public abstract class AbstractServiceProvider<T> implements ServiceProvider<T> {
private final @Nullable T service = load();
abstract protected T load();
@Override
public T get() {
return service;
}
}

View file

@ -0,0 +1,21 @@
package de.kentoj.scrow.provider.provider;
import java.util.function.Supplier;
public class GenericServiceProvider<T> implements ServiceProvider<T> {
private final T service;
public GenericServiceProvider(T service) {
this.service = service;
}
public GenericServiceProvider(Supplier<T> serviceSupplier) {
this.service = serviceSupplier.get();
}
@Override
public T get() {
return service;
}
}

View file

@ -0,0 +1,8 @@
package de.kentoj.scrow.provider.provider;
public interface ServiceProvider<T> {
T get();
default void cleanup() {}
}

View file

@ -7,21 +7,27 @@ import java.util.UUID;
public interface EconomyService {
/**
* @throws IllegalArgumentException if amount < 0
*/
Mono<@NotNull Integer> getCoins(UUID playerId);
/**
* @param playerId uuid of player
* @throws IllegalArgumentException if amount < 0
*/
Mono<@NotNull Void> depositCoins(UUID playerId, int amount);
/**
* @param playerId uuid of player
* @return true on success, false on insufficient funds
* @throws IllegalArgumentException if amount < 0
*/
Mono<@NotNull Boolean> tryWithdrawCoins(UUID playerId, int amount);
/**
* @param playerId uuid of player
* @throws IllegalArgumentException if amount < 0
*/
Mono<@NotNull Void> setCoins(UUID playerId, int amount);
}

View file

@ -0,0 +1,10 @@
package de.kentoj.scrow.services.friends;
import java.time.Instant;
import java.util.UUID;
public interface FriendRequest {
UUID getFrom();
UUID getTo();
Instant getCreatedAt();
}

View file

@ -0,0 +1,11 @@
package de.kentoj.scrow.services.friends;
import de.kentoj.scrow.util.pair.Tuple2;
import java.time.Instant;
import java.util.UUID;
public interface Friendship {
Tuple2<UUID, UUID> getPlayerIds();
Instant getCreatedAt();
}

View file

@ -0,0 +1,9 @@
package de.kentoj.scrow.util.pair;
import lombok.Value;
@Value
public class Tuple2<A, B> {
A first;
B second;
}

View file

@ -0,0 +1,10 @@
package de.kentoj.scrow.util.pair;
import lombok.Value;
@Value
public class Tuple3<A, B, C> {
A first;
B second;
C third;
}

View file

@ -0,0 +1,11 @@
package de.kentoj.scrow.util.pair;
import lombok.Value;
@Value
public class Tuple4<A, B, C, D> {
A first;
B second;
C third;
D fourth;
}