mirror of
https://github.com/CivMC/Civ.git
synced 2026-07-15 23:20:44 +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" }
|
cron-utils = { group = "com.cronutils", name = "cron-utils", version = "9.2.1" }
|
||||||
|
|
||||||
configurate-yaml = { group = "org.spongepowered", name = "configurate-yaml", version.ref = "configurate" }
|
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" }
|
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"))
|
compileOnly(project(":plugins:civmodcore-paper"))
|
||||||
api(project(":libraries:name-api"))
|
api(project(":libraries:name-api"))
|
||||||
|
api(project(":libraries:namelayer-sync"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ plugins {
|
|||||||
include(":ansible")
|
include(":ansible")
|
||||||
|
|
||||||
include(":libraries:name-api")
|
include(":libraries:name-api")
|
||||||
|
include(":libraries:namelayer-sync")
|
||||||
|
|
||||||
include(":plugins:announcements-velocity")
|
include(":plugins:announcements-velocity")
|
||||||
include(":plugins:banstick-paper")
|
include(":plugins:banstick-paper")
|
||||||
|
|||||||
Reference in New Issue
Block a user