mirror of
https://github.com/CivMC/Civ.git
synced 2026-07-15 15:18:04 +00:00
add namelayer sync message contracts
This commit is contained in:
@@ -24,6 +24,7 @@ commons-math3 = { group = "org.apache.commons", name = "commons-math3", version
|
||||
cron-utils = { group = "com.cronutils", name = "cron-utils", version = "9.2.1" }
|
||||
|
||||
configurate-yaml = { group = "org.spongepowered", name = "configurate-yaml", version.ref = "configurate" }
|
||||
gson = { group = "com.google.code.gson", name = "gson", version = "2.13.2" }
|
||||
|
||||
rabbitmq-client = { group = "com.rabbitmq", name = "amqp-client", version = "5.26.0" }
|
||||
|
||||
|
||||
7
libraries/namelayer-sync/build.gradle.kts
Normal file
7
libraries/namelayer-sync/build.gradle.kts
Normal file
@@ -0,0 +1,7 @@
|
||||
plugins {
|
||||
id("java-library")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(libs.gson)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package net.civmc.namelayer.sync;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public record NameLayerInvalidationMessage(Set<Integer> affectedGroupIds, boolean requiresFullResync) {
|
||||
|
||||
public NameLayerInvalidationMessage {
|
||||
affectedGroupIds = Set.copyOf(validateGroupIds(affectedGroupIds));
|
||||
if (!requiresFullResync && affectedGroupIds.isEmpty()) {
|
||||
throw new IllegalArgumentException("affectedGroupIds must not be empty unless requiresFullResync is true");
|
||||
}
|
||||
}
|
||||
|
||||
public static NameLayerInvalidationMessage targeted(final Set<Integer> affectedGroupIds) {
|
||||
return new NameLayerInvalidationMessage(affectedGroupIds, false);
|
||||
}
|
||||
|
||||
public static NameLayerInvalidationMessage fullResync() {
|
||||
return new NameLayerInvalidationMessage(Set.of(), true);
|
||||
}
|
||||
|
||||
private static Set<Integer> validateGroupIds(final Set<Integer> groupIds) {
|
||||
Objects.requireNonNull(groupIds, "affectedGroupIds");
|
||||
for (final Integer groupId : groupIds) {
|
||||
if (groupId == null || groupId <= 0) {
|
||||
throw new IllegalArgumentException("affectedGroupIds must contain only positive group IDs");
|
||||
}
|
||||
}
|
||||
return groupIds;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.civmc.namelayer.sync;
|
||||
|
||||
public final class NameLayerRabbitMqTopology {
|
||||
|
||||
public static final String CONTENT_TYPE_JSON = "application/json";
|
||||
public static final String INVALIDATION_EXCHANGE = "namelayer.invalidations";
|
||||
public static final String INVALIDATION_EXCHANGE_TYPE = "fanout";
|
||||
public static final boolean INVALIDATION_EXCHANGE_DURABLE = true;
|
||||
public static final String PAPER_INVALIDATION_QUEUE_PREFIX = "namelayer.invalidations.";
|
||||
public static final String WRITE_REQUEST_QUEUE = "namelayer.write.requests";
|
||||
public static final boolean WRITE_REQUEST_QUEUE_DURABLE = true;
|
||||
public static final String WRITE_RESPONSE_QUEUE_PREFIX = "namelayer.write.responses.";
|
||||
|
||||
private NameLayerRabbitMqTopology() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package net.civmc.namelayer.sync;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class NameLayerSyncCodec {
|
||||
|
||||
private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();
|
||||
|
||||
private NameLayerSyncCodec() {
|
||||
}
|
||||
|
||||
public static byte[] encodeWriteRequest(final NameLayerWriteRequest request) {
|
||||
return encode(request);
|
||||
}
|
||||
|
||||
public static NameLayerWriteRequest decodeWriteRequest(final byte[] body) {
|
||||
return decode(body, NameLayerWriteRequest.class);
|
||||
}
|
||||
|
||||
public static byte[] encodeWriteResponse(final NameLayerWriteResponse response) {
|
||||
return encode(response);
|
||||
}
|
||||
|
||||
public static NameLayerWriteResponse decodeWriteResponse(final byte[] body) {
|
||||
return decode(body, NameLayerWriteResponse.class);
|
||||
}
|
||||
|
||||
public static byte[] encodeInvalidation(final NameLayerInvalidationMessage invalidation) {
|
||||
return encode(invalidation);
|
||||
}
|
||||
|
||||
public static NameLayerInvalidationMessage decodeInvalidation(final byte[] body) {
|
||||
return decode(body, NameLayerInvalidationMessage.class);
|
||||
}
|
||||
|
||||
private static byte[] encode(final Object message) {
|
||||
Objects.requireNonNull(message, "message");
|
||||
return GSON.toJson(message).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static <T> T decode(final byte[] body, final Class<T> messageType) {
|
||||
Objects.requireNonNull(body, "body");
|
||||
Objects.requireNonNull(messageType, "messageType");
|
||||
try {
|
||||
return GSON.fromJson(new String(body, StandardCharsets.UTF_8), messageType);
|
||||
} catch (final JsonParseException exception) {
|
||||
throw new IllegalArgumentException("Invalid NameLayer synchronization message", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package net.civmc.namelayer.sync;
|
||||
|
||||
public enum NameLayerWriteFailureCode {
|
||||
AUTHORIZATION_FAILED,
|
||||
DATABASE_UNAVAILABLE,
|
||||
GROUP_NOT_FOUND,
|
||||
INVALID_REQUEST,
|
||||
MEMBER_NOT_FOUND,
|
||||
NAME_CONFLICT,
|
||||
PLAYER_NOT_FOUND,
|
||||
UNKNOWN_OPERATION,
|
||||
WRITE_FAILED
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.civmc.namelayer.sync;
|
||||
|
||||
public enum NameLayerWriteOperation {
|
||||
ADD_BLACKLIST,
|
||||
ADD_INVITATION,
|
||||
ADD_PERMISSION,
|
||||
ACCEPT_INVITATION,
|
||||
CREATE_GROUP,
|
||||
DELETE_GROUP,
|
||||
JOIN_GROUP,
|
||||
LEAVE_GROUP,
|
||||
REMOVE_BLACKLIST,
|
||||
REMOVE_INVITATION,
|
||||
REMOVE_MEMBER,
|
||||
REMOVE_PERMISSION,
|
||||
RENAME_GROUP,
|
||||
SET_AUTO_ACCEPT,
|
||||
SET_DEFAULT_GROUP,
|
||||
SET_GROUP_COLOR,
|
||||
SET_GROUP_DISCIPLINE,
|
||||
SET_GROUP_OWNER,
|
||||
SET_GROUP_PASSWORD,
|
||||
SET_MEMBER_ROLE
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package net.civmc.namelayer.sync;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public record NameLayerWriteRequest(
|
||||
UUID requestId,
|
||||
String originServerId,
|
||||
UUID actorUuid,
|
||||
NameLayerWriteOperation operation,
|
||||
Map<String, String> arguments,
|
||||
long createdAtEpochMillis
|
||||
) {
|
||||
|
||||
public NameLayerWriteRequest {
|
||||
Objects.requireNonNull(requestId, "requestId");
|
||||
originServerId = requireNonBlank(originServerId, "originServerId");
|
||||
Objects.requireNonNull(actorUuid, "actorUuid");
|
||||
Objects.requireNonNull(operation, "operation");
|
||||
arguments = Map.copyOf(Objects.requireNonNull(arguments, "arguments"));
|
||||
if (createdAtEpochMillis <= 0) {
|
||||
throw new IllegalArgumentException("createdAtEpochMillis must be positive");
|
||||
}
|
||||
}
|
||||
|
||||
public static NameLayerWriteRequest create(
|
||||
final String originServerId,
|
||||
final UUID actorUuid,
|
||||
final NameLayerWriteOperation operation,
|
||||
final Map<String, String> arguments
|
||||
) {
|
||||
return new NameLayerWriteRequest(
|
||||
UUID.randomUUID(),
|
||||
originServerId,
|
||||
actorUuid,
|
||||
operation,
|
||||
new LinkedHashMap<>(arguments),
|
||||
System.currentTimeMillis()
|
||||
);
|
||||
}
|
||||
|
||||
private static String requireNonBlank(final String value, final String fieldName) {
|
||||
Objects.requireNonNull(value, fieldName);
|
||||
final String trimmed = value.trim();
|
||||
if (trimmed.isEmpty()) {
|
||||
throw new IllegalArgumentException(fieldName + " must not be blank");
|
||||
}
|
||||
return trimmed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.civmc.namelayer.sync;
|
||||
|
||||
public final class NameLayerWriteRequestPolicy {
|
||||
|
||||
public static final long DEFAULT_TIMEOUT_MILLIS = 5_000L;
|
||||
public static final int AUTOMATIC_RETRY_ATTEMPTS = 0;
|
||||
|
||||
private NameLayerWriteRequestPolicy() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package net.civmc.namelayer.sync;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public record NameLayerWriteResponse(
|
||||
UUID requestId,
|
||||
boolean success,
|
||||
NameLayerWriteFailureCode failureCode,
|
||||
String message,
|
||||
Set<Integer> affectedGroupIds,
|
||||
boolean requiresFullResync,
|
||||
long completedAtEpochMillis
|
||||
) {
|
||||
|
||||
public NameLayerWriteResponse {
|
||||
Objects.requireNonNull(requestId, "requestId");
|
||||
if (success && failureCode != null) {
|
||||
throw new IllegalArgumentException("successful responses must not include a failureCode");
|
||||
}
|
||||
if (!success) {
|
||||
Objects.requireNonNull(failureCode, "failureCode");
|
||||
}
|
||||
message = message == null ? "" : message;
|
||||
affectedGroupIds = Set.copyOf(validateGroupIds(affectedGroupIds));
|
||||
if (completedAtEpochMillis <= 0) {
|
||||
throw new IllegalArgumentException("completedAtEpochMillis must be positive");
|
||||
}
|
||||
}
|
||||
|
||||
public static NameLayerWriteResponse success(final UUID requestId, final Set<Integer> affectedGroupIds) {
|
||||
return new NameLayerWriteResponse(
|
||||
requestId,
|
||||
true,
|
||||
null,
|
||||
"",
|
||||
affectedGroupIds,
|
||||
false,
|
||||
System.currentTimeMillis()
|
||||
);
|
||||
}
|
||||
|
||||
public static NameLayerWriteResponse failure(
|
||||
final UUID requestId,
|
||||
final NameLayerWriteFailureCode failureCode,
|
||||
final String message
|
||||
) {
|
||||
return new NameLayerWriteResponse(
|
||||
requestId,
|
||||
false,
|
||||
failureCode,
|
||||
message,
|
||||
Set.of(),
|
||||
false,
|
||||
System.currentTimeMillis()
|
||||
);
|
||||
}
|
||||
|
||||
private static Set<Integer> validateGroupIds(final Set<Integer> groupIds) {
|
||||
Objects.requireNonNull(groupIds, "affectedGroupIds");
|
||||
for (final Integer groupId : groupIds) {
|
||||
if (groupId == null || groupId <= 0) {
|
||||
throw new IllegalArgumentException("affectedGroupIds must contain only positive group IDs");
|
||||
}
|
||||
}
|
||||
return groupIds;
|
||||
}
|
||||
}
|
||||
@@ -12,4 +12,5 @@ dependencies {
|
||||
|
||||
compileOnly(project(":plugins:civmodcore-paper"))
|
||||
api(project(":libraries:name-api"))
|
||||
api(project(":libraries:namelayer-sync"))
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ plugins {
|
||||
include(":ansible")
|
||||
|
||||
include(":libraries:name-api")
|
||||
include(":libraries:namelayer-sync")
|
||||
|
||||
include(":plugins:announcements-velocity")
|
||||
include(":plugins:banstick-paper")
|
||||
|
||||
Reference in New Issue
Block a user