This commit is contained in:
kento2 2026-08-05 21:40:52 +02:00
parent c60f19cafc
commit 2ea2eb5d1e
114 changed files with 671 additions and 609 deletions

View file

@ -19,5 +19,4 @@ java_library(
artifact("io.nats:jnats"),
],
exports = shared_deps,
plugins = [ "//third_party:lombok_plugin" ],
)

View file

@ -1,28 +1,21 @@
package de.kentoj.scrowlib.convention;
import de.kentoj.kencommandapi.api.platform.MessageStyle;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
public class ScrowMessageStyle implements MessageStyle {
public record ScrowMessageStyle(
Component prefix,
String prefixText,
TextColor prefixColor
) implements MessageStyle {
public static ScrowMessageStyle GENERIC = new ScrowMessageStyle("Scrow", TextColor.color(0xe5377f));
@Getter
private final Component prefix;
@Getter
private final String prefixText;
@Getter
private final TextColor prefixColor;
public ScrowMessageStyle(String prefixText, TextColor prefixColor) {
this.prefixText = prefixText;
this.prefixColor = prefixColor;
this.prefix = Component.text(prefixText)
.color(prefixColor);
this(Component.text(prefixText).color(prefixColor), prefixText, prefixColor);
}
@Override

View file

@ -4,7 +4,7 @@ import java.time.Instant;
import java.util.UUID;
public interface FriendRequest {
UUID getFrom();
UUID getTo();
Instant getCreatedAt();
UUID from();
UUID to();
Instant createdAt();
}

View file

@ -4,7 +4,7 @@ import java.time.Instant;
public interface Friendship {
OrderedUUIDPair getUuids();
OrderedUUIDPair uuids();
Instant getCreatedAt();
Instant createdAt();
}

View file

@ -1,25 +1,24 @@
package de.kentoj.scrowlib.friends;
import lombok.Getter;
import java.util.UUID;
@Getter
public class OrderedUUIDPair {
private final UUID first;
private final UUID second;
public OrderedUUIDPair(UUID uuid1, UUID uuid2) {
if (uuid1.compareTo(uuid2) < 0) {
this.first = uuid1;
this.second = uuid2;
public record OrderedUUIDPair(
UUID first,
UUID second
) {
public static OrderedUUIDPair of(UUID a, UUID b) {
UUID first, second;
if (a.compareTo(b) < 0) {
first = a;
second = b;
} else {
this.first = uuid2;
this.second = uuid1;
first = b;
second = a;
}
return new OrderedUUIDPair(first, second);
}
public UUID getOther(UUID self) {
return getFirst().equals(self) ? getSecond() : getFirst();
return first().equals(self) ? second() : first();
}
}

View file

@ -3,18 +3,15 @@ package de.kentoj.scrowlib.friends.friendship;
import de.kentoj.scrowlib.friends.Friendship;
import de.kentoj.scrowlib.friends.OrderedUUIDPair;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import java.time.Instant;
import java.util.UUID;
@Value
@RequiredArgsConstructor
public class FriendshipImpl implements Friendship {
OrderedUUIDPair uuids;
Instant createdAt;
public record FriendshipImpl(
OrderedUUIDPair uuids,
Instant createdAt
) implements Friendship {
public FriendshipImpl(UUID uuid1, UUID uuid2, Instant createdAt) {
this(new OrderedUUIDPair(uuid1, uuid2), createdAt);
this(OrderedUUIDPair.of(uuid1, uuid2), createdAt);
}
}

View file

@ -15,14 +15,14 @@ public class InMemoryFriendshipService implements FriendshipService {
@Override
public Friendship getFriendship(UUID uuid1, UUID uuid2) {
return friendships.get(new OrderedUUIDPair(uuid1, uuid2));
return friendships.get(OrderedUUIDPair.of(uuid1, uuid2));
}
@Override
public List<Friendship> getFriendships(UUID playerId) {
return friendships.entrySet().stream()
.filter(ent ->
ent.getKey().getFirst().equals(playerId) || ent.getKey().getSecond().equals(playerId)
ent.getKey().first().equals(playerId) || ent.getKey().second().equals(playerId)
)
.map(Map.Entry::getValue)
.toList();
@ -30,11 +30,11 @@ public class InMemoryFriendshipService implements FriendshipService {
@Override
public boolean saveFriendship(Friendship friendship) {
return friendships.putIfAbsent(friendship.getUuids(), friendship) == null;
return friendships.putIfAbsent(friendship.uuids(), friendship) == null;
}
@Override
public boolean deleteFriendship(UUID uuid1, UUID uuid2) {
return friendships.remove(new OrderedUUIDPair(uuid1, uuid2)) != null;
return friendships.remove(OrderedUUIDPair.of(uuid1, uuid2)) != null;
}
}

View file

@ -33,15 +33,15 @@ public class PostgresFriendshipService implements FriendshipService {
@Override
public @Nullable Friendship getFriendship(UUID uuid1, UUID uuid2) {
var uuids = new OrderedUUIDPair(uuid1, uuid2);
var uuids = OrderedUUIDPair.of(uuid1, uuid2);
return jdbi.withHandle(h -> h.createQuery("""
SELECT first_uuid, second_uuid, created_at
FROM friendships
WHERE first_uuid = :first AND second_uuid = :second
""")
.bind("first", uuids.getFirst())
.bind("second", uuids.getSecond())
.bind("first", uuids.first())
.bind("second", uuids.second())
.map(this::parseFriendship)
.findFirst().orElse(null));
}
@ -65,21 +65,21 @@ public class PostgresFriendshipService implements FriendshipService {
VALUES (:first, :second, :createdAt)
ON CONFLICT (first_uuid, second_uuid) DO NOTHING
""")
.bind("first", friendship.getUuids().getFirst())
.bind("second", friendship.getUuids().getSecond())
.bind("createdAt", friendship.getCreatedAt())
.bind("first", friendship.uuids().first())
.bind("second", friendship.uuids().second())
.bind("createdAt", friendship.createdAt())
.execute() > 0);
}
@Override
public boolean deleteFriendship(UUID uuid1, UUID uuid2) {
var uuids = new OrderedUUIDPair(uuid1, uuid2);
var uuids = OrderedUUIDPair.of(uuid1, uuid2);
return jdbi.withHandle(h -> h.createUpdate("""
DELETE FROM friendships
WHERE first_uuid = :first AND second_uuid = :second
""")
.bind("first", uuids.getFirst())
.bind("second", uuids.getSecond())
.bind("first", uuids.first())
.bind("second", uuids.second())
.execute() > 0);
}

View file

@ -1,19 +1,15 @@
package de.kentoj.scrowlib.friends.request;
import de.kentoj.scrowlib.friends.FriendRequest;
import lombok.AllArgsConstructor;
import lombok.Value;
import java.time.Instant;
import java.util.UUID;
@Value
@AllArgsConstructor
public class FriendRequestImpl implements FriendRequest {
UUID from;
UUID to;
Instant createdAt;
public record FriendRequestImpl(
UUID from,
UUID to,
Instant createdAt
) implements FriendRequest {
public FriendRequestImpl(UUID from, UUID to) {
this(from, to, Instant.now());
}

View file

@ -15,21 +15,21 @@ public class InMemoryFriendRequestService implements FriendRequestService {
@Override
public List<FriendRequest> getFriendRequestsTo(UUID playerId) {
return requests.stream()
.filter(req -> req.getTo().equals(playerId))
.filter(req -> req.to().equals(playerId))
.toList();
}
@Override
public List<FriendRequest> getFriendRequestsFrom(UUID playerId) {
return requests.stream()
.filter(req -> req.getFrom().equals(playerId))
.filter(req -> req.from().equals(playerId))
.toList();
}
@Override
public FriendRequest getFriendRequest(UUID from, UUID to) {
return requests.stream()
.filter(req -> req.getFrom().equals(from) && req.getTo().equals(to))
.filter(req -> req.from().equals(from) && req.to().equals(to))
.findFirst()
.orElse(null);
}
@ -41,6 +41,6 @@ public class InMemoryFriendRequestService implements FriendRequestService {
@Override
public boolean deleteFriendRequest(UUID from, UUID to) {
return requests.removeIf(req -> req.getFrom().equals(from) && req.getTo().equals(to));
return requests.removeIf(req -> req.from().equals(from) && req.to().equals(to));
}
}

View file

@ -13,7 +13,6 @@ import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;
@Slf4j
public class PostgresFriendRequestService implements FriendRequestService {
private final Jdbi jdbi;
@ -52,9 +51,9 @@ public class PostgresFriendRequestService implements FriendRequestService {
INSERT INTO friendrequests (from_uuid, to_uuid, created_at)
VALUES (:from, :to, :createdAt)
""")
.bind("from", friendRequest.getFrom())
.bind("to", friendRequest.getTo())
.bind("createdAt", friendRequest.getCreatedAt())
.bind("from", friendRequest.from())
.bind("to", friendRequest.to())
.bind("createdAt", friendRequest.createdAt())
.execute() > 0);
}

View file

@ -24,7 +24,7 @@ public interface InstanceManager {
*
* @return list of running instances
*/
List<ServerInstance> getInstances();
List<ServerInstance> instances();
/**
* Runs blocking

View file

@ -8,11 +8,14 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
@RequiredArgsConstructor
public class InstanceManagerImpl implements InstanceManager {
private final MessageBroker messageBroker;
public InstanceManagerImpl(MessageBroker messageBroker) {
this.messageBroker = messageBroker;
}
@Override
public ServerInstance deployInstance(String template) {
var document = messageBroker.request("instance.deploy", new Document("template", template));
@ -31,7 +34,7 @@ public class InstanceManagerImpl implements InstanceManager {
}
@Override
public List<ServerInstance> getInstances() {
public List<ServerInstance> instances() {
var document = messageBroker.request("instance.list", new Document());
return document.getList("instances", Document.class)
.stream()

View file

@ -1,21 +1,17 @@
package de.kentoj.scrowlib.instancemanager;
import com.google.common.base.Preconditions;
import lombok.Value;
import org.bson.Document;
@Value
public class ServerInstance {
String handle;
String template;
int port;
import static com.google.common.base.Preconditions.checkNotNull;
public ServerInstance(String handle, String template, int port) {
Preconditions.checkNotNull(template);
Preconditions.checkNotNull(handle);
this.template = template;
this.handle = handle;
this.port = port;
public record ServerInstance(
String handle,
String template,
int port
) {
public ServerInstance {
checkNotNull(template);
checkNotNull(handle);
}
static ServerInstance fromDocument(Document document) {

View file

@ -1,7 +1,8 @@
package de.kentoj.scrowlib.messaging;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
@ -10,9 +11,10 @@ import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@Slf4j
public class InMemoyMessageBroker implements MessageBroker {
private final Logger log = LoggerFactory.getLogger(InMemoyMessageBroker.class);
private final Map<String, List<Consumer<Document>>> subscribers = new HashMap<>();
private final Map<String, RequestHandler> handlers = new HashMap<>();
@ -25,7 +27,7 @@ public class InMemoyMessageBroker implements MessageBroker {
@Override
public void subscribe(String subject, Consumer<Document> consumer) {
subscribers.computeIfAbsent(subject, __ -> new ArrayList<>())
subscribers.computeIfAbsent(subject, _ -> new ArrayList<>())
.add(consumer);
}

View file

@ -15,6 +15,7 @@ public interface MessageBroker {
void publish(String subject, Document document);
/**
* May block until subscription registered.
* subscribes to a subject, consumer may run asynchronously
*
* @param subject <a href="https://docs.nats.io/nats-concepts/subjects">subject</a> to publish to

View file

@ -5,8 +5,9 @@ import com.leakyabstractions.result.core.Results;
import de.kentoj.scrowlib.utils.DocumentRepository;
import io.nats.client.Connection;
import io.nats.client.Dispatcher;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
@ -14,9 +15,10 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
@Slf4j
public class NatsMessageBroker implements MessageBroker {
private final Logger log = LoggerFactory.getLogger(NatsMessageBroker.class);
private static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
private final Connection nats;
private final Dispatcher dispatcher;

View file

@ -2,14 +2,14 @@ package de.kentoj.scrowlib.messaging;
import com.leakyabstractions.result.api.Result;
import com.leakyabstractions.result.core.Results;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.bson.Document;
@NoArgsConstructor(access = AccessLevel.NONE)
class ResultRepository {
private ResultRepository() {
}
static Document toDocument(Result<Document, String> result) {
return result.hasSuccess()
? new Document("data", result.getSuccess().orElseThrow())

View file

@ -4,6 +4,5 @@ import java.util.UUID;
@FunctionalInterface
public interface NetworkPlayerFactory {
NetworkPlayer create(UUID uuid);
}

View file

@ -12,13 +12,17 @@ import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@RequiredArgsConstructor
public class NetworkPlayerImpl implements NetworkPlayer {
private static final MiniMessage MM = MiniMessage.miniMessage();
private static final ExecutorService EXECUTOR = Executors.newVirtualThreadPerTaskExecutor();
private final MessageBroker messageBroker;
private final UUID playerId;
public NetworkPlayerImpl(MessageBroker messageBroker, UUID playerId) {
this.messageBroker = messageBroker;
this.playerId = playerId;
}
@Override
public void sendMessage(@NotNull Component message) {
requestAsync("player.sendMessage", new Document("message", MM.serialize(message)));

View file

@ -1,21 +1,22 @@
package de.kentoj.scrowlib.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig;
import net.kyori.adventure.text.event.ClickEvent;
import java.util.regex.Pattern;
@NoArgsConstructor(access = AccessLevel.NONE)
public class ComponentUtils {
private static final Pattern URL_REGEX = Pattern.compile("(https?://)?[a-z0-9]+(\\.[a-z0-9]+)*(\\.[a-z0-9]{1,10})((/+)[^/ ]*)*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
private static final TextReplacementConfig REPLACER = TextReplacementConfig.builder()
.match(URL_REGEX)
.replacement((c) -> c.clickEvent(ClickEvent.openUrl(c.content().startsWith("http") ? c.content() : "https://" + c.content())))
.build();
private ComponentUtils() {
}
public static Component resolveURLS(Component component) {
return component.replaceText(REPLACER);

View file

@ -1,13 +1,13 @@
package de.kentoj.scrowlib.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.bson.Document;
import org.jetbrains.annotations.ApiStatus;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class DocumentRepository {
private DocumentRepository() {
}
@ApiStatus.Obsolete
public static Document fromBytes(byte[] bytes) {
return Document.parse(new String(bytes));

View file

@ -1,10 +1,10 @@
package de.kentoj.scrowlib.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
@NoArgsConstructor(access = AccessLevel.NONE)
public class EnvUtils {
private EnvUtils() {
}
/**
* @throws IllegalStateException if the var is not set
*/