Connection.java

net.minecraft.server.jsonrpc.Connection

信息

  • 全限定名:net.minecraft.server.jsonrpc.Connection
  • 类型:public class
  • 包:net.minecraft.server.jsonrpc
  • 源码路径:src/main/java/net/minecraft/server/jsonrpc/Connection.java
  • 起始行号:L40
  • 继承:SimpleChannelInboundHandler
  • 职责:

    TODO

字段/常量

  • LOGGER

    • 类型: Logger
    • 修饰符: private static final
    • 源码定位: L41
    • 说明:

      TODO

  • CONNECTION_ID_COUNTER

    • 类型: AtomicInteger
    • 修饰符: private static final
    • 源码定位: L42
    • 说明:

      TODO

  • jsonRpcLogger

    • 类型: JsonRpcLogger
    • 修饰符: private final
    • 源码定位: L43
    • 说明:

      TODO

  • clientInfo

    • 类型: ClientInfo
    • 修饰符: private final
    • 源码定位: L44
    • 说明:

      TODO

  • managementServer

    • 类型: ManagementServer
    • 修饰符: private final
    • 源码定位: L45
    • 说明:

      TODO

  • channel

    • 类型: Channel
    • 修饰符: private final
    • 源码定位: L46
    • 说明:

      TODO

  • minecraftApi

    • 类型: MinecraftApi
    • 修饰符: private final
    • 源码定位: L47
    • 说明:

      TODO

  • transactionId

    • 类型: AtomicInteger
    • 修饰符: private final
    • 源码定位: L48
    • 说明:

      TODO

  • pendingRequests

    • 类型: Int2ObjectMap<PendingRpcRequest<?>>
    • 修饰符: private final
    • 源码定位: L49
    • 说明:

      TODO

内部类/嵌套类型

构造器

public Connection(Channel channel, ManagementServer managementServer, MinecraftApi minecraftApi, JsonRpcLogger jsonrpcLogger) @ L51

  • 构造器名:Connection
  • 源码定位:L51
  • 修饰符:public

参数:

  • channel: Channel
  • managementServer: ManagementServer
  • minecraftApi: MinecraftApi
  • jsonrpcLogger: JsonRpcLogger

说明:

TODO

方法

下面的方法块按源码顺序生成。

public void tick() @ L59

  • 方法名:tick
  • 源码定位:L59
  • 返回类型:void
  • 修饰符:public

参数:

说明:

TODO

public void channelActive(ChannelHandlerContext ctx) @ L79

  • 方法名:channelActive
  • 源码定位:L79
  • 返回类型:void
  • 修饰符:public

参数:

  • ctx: ChannelHandlerContext

说明:

TODO

public void channelInactive(ChannelHandlerContext ctx) @ L86

  • 方法名:channelInactive
  • 源码定位:L86
  • 返回类型:void
  • 修饰符:public

参数:

  • ctx: ChannelHandlerContext

说明:

TODO

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) @ L93

  • 方法名:exceptionCaught
  • 源码定位:L93
  • 返回类型:void
  • 修饰符:public

参数:

  • ctx: ChannelHandlerContext
  • cause: Throwable

说明:

TODO

protected void channelRead0(ChannelHandlerContext channelHandlerContext, JsonElement jsonElement) @ L103

  • 方法名:channelRead0
  • 源码定位:L103
  • 返回类型:void
  • 修饰符:protected

参数:

  • channelHandlerContext: ChannelHandlerContext
  • jsonElement: JsonElement

说明:

TODO

private JsonArray handleBatchRequest(List<JsonElement> batchRequests) @ L116

  • 方法名:handleBatchRequest
  • 源码定位:L116
  • 返回类型:JsonArray
  • 修饰符:private

参数:

  • batchRequests: List

说明:

TODO

public void sendNotification(Holder.Reference<?extends OutgoingRpcMethod<Void,?>> method) @ L122

  • 方法名:sendNotification
  • 源码定位:L122
  • 返回类型:void
  • 修饰符:public

参数:

  • method: Holder.Reference>

说明:

TODO

public <Params> void sendNotification(Holder.Reference<?extends OutgoingRpcMethod<Params,?>> method, Params params) @ L126

  • 方法名:sendNotification
  • 源码定位:L126
  • 返回类型: void
  • 修饰符:public

参数:

  • method: Holder.Reference>
  • params: Params

说明:

TODO

public <Result> CompletableFuture<Result> sendRequest(Holder.Reference<?extends OutgoingRpcMethod<Void,Result>> method) @ L130

  • 方法名:sendRequest
  • 源码定位:L130
  • 返回类型: CompletableFuture
  • 修饰符:public

参数:

  • method: Holder.Reference<?extends OutgoingRpcMethod<Void,Result>>

说明:

TODO

public <Params,Result> CompletableFuture<Result> sendRequest(Holder.Reference<?extends OutgoingRpcMethod<Params,Result>> method, Params params) @ L134

  • 方法名:sendRequest
  • 源码定位:L134
  • 返回类型:<Params,Result> CompletableFuture
  • 修饰符:public

参数:

  • method: Holder.Reference<?extends OutgoingRpcMethod<Params,Result>>
  • params: Params

说明:

TODO

private <Params,Result> CompletableFuture<Result> sendRequest(Holder.Reference<?extends OutgoingRpcMethod<Params,?extends Result>> method, Params params, boolean expectReply) @ L138

  • 方法名:sendRequest
  • 源码定位:L138
  • 返回类型:<Params,Result> CompletableFuture
  • 修饰符:private

参数:

  • method: Holder.Reference<?extends OutgoingRpcMethod<Params,?extends Result>>
  • params: Params
  • expectReply: boolean

说明:

TODO

JsonObject handleJsonObject(JsonObject jsonObject) @ L156

  • 方法名:handleJsonObject
  • 源码定位:L156
  • 返回类型:JsonObject
  • 修饰符:package-private

参数:

  • jsonObject: JsonObject

说明:

TODO

private static boolean isValidRequestId(JsonElement id) @ L187

  • 方法名:isValidRequestId
  • 源码定位:L187
  • 返回类型:boolean
  • 修饰符:private static

参数:

  • id: JsonElement

说明:

TODO

private static boolean isValidResponseId(JsonElement id) @ L191

  • 方法名:isValidResponseId
  • 源码定位:L191
  • 返回类型:boolean
  • 修饰符:private static

参数:

  • id: JsonElement

说明:

TODO

private JsonObject handleIncomingRequest(JsonElement id, String method, JsonElement params) @ L195

  • 方法名:handleIncomingRequest
  • 源码定位:L195
  • 返回类型:JsonObject
  • 修饰符:private

参数:

  • id: JsonElement
  • method: String
  • params: JsonElement

说明:

TODO

public JsonElement dispatchIncomingRequest(String method, JsonElement params) @ L217

  • 方法名:dispatchIncomingRequest
  • 源码定位:L217
  • 返回类型:JsonElement
  • 修饰符:public

参数:

  • method: String
  • params: JsonElement

说明:

TODO

private void handleRequestResponse(int id, JsonElement result) @ L241

  • 方法名:handleRequestResponse
  • 源码定位:L241
  • 返回类型:void
  • 修饰符:private

参数:

  • id: int
  • result: JsonElement

说明:

TODO

private JsonObject handleError(JsonElement id, JsonObject error) @ L250

  • 方法名:handleError
  • 源码定位:L250
  • 返回类型:JsonObject
  • 修饰符:private

参数:

  • id: JsonElement
  • error: JsonObject

说明:

TODO

代码

public class Connection extends SimpleChannelInboundHandler<JsonElement> {
    private static final Logger LOGGER = LogUtils.getLogger();
    private static final AtomicInteger CONNECTION_ID_COUNTER = new AtomicInteger(0);
    private final JsonRpcLogger jsonRpcLogger;
    private final ClientInfo clientInfo;
    private final ManagementServer managementServer;
    private final Channel channel;
    private final MinecraftApi minecraftApi;
    private final AtomicInteger transactionId = new AtomicInteger();
    private final Int2ObjectMap<PendingRpcRequest<?>> pendingRequests = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>());
 
    public Connection(Channel channel, ManagementServer managementServer, MinecraftApi minecraftApi, JsonRpcLogger jsonrpcLogger) {
        this.clientInfo = ClientInfo.of(CONNECTION_ID_COUNTER.incrementAndGet());
        this.managementServer = managementServer;
        this.minecraftApi = minecraftApi;
        this.channel = channel;
        this.jsonRpcLogger = jsonrpcLogger;
    }
 
    public void tick() {
        long time = Util.getMillis();
        this.pendingRequests
            .int2ObjectEntrySet()
            .removeIf(
                entry -> {
                    boolean timedOut = entry.getValue().timedOut(time);
                    if (timedOut) {
                        entry.getValue()
                            .resultFuture()
                            .completeExceptionally(
                                new ReadTimeoutException("RPC method " + entry.getValue().method().key().identifier() + " timed out waiting for response")
                            );
                    }
 
                    return timedOut;
                }
            );
    }
 
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        this.jsonRpcLogger.log(this.clientInfo, "Management connection opened for {}", this.channel.remoteAddress());
        super.channelActive(ctx);
        this.managementServer.onConnected(this);
    }
 
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        this.jsonRpcLogger.log(this.clientInfo, "Management connection closed for {}", this.channel.remoteAddress());
        super.channelInactive(ctx);
        this.managementServer.onDisconnected(this);
    }
 
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if (cause.getCause() instanceof JsonParseException) {
            this.channel.writeAndFlush(JsonRPCErrors.PARSE_ERROR.createWithUnknownId(cause.getMessage()));
        } else {
            super.exceptionCaught(ctx, cause);
            this.channel.close().awaitUninterruptibly();
        }
    }
 
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, JsonElement jsonElement) {
        if (jsonElement.isJsonObject()) {
            JsonObject response = this.handleJsonObject(jsonElement.getAsJsonObject());
            if (response != null) {
                this.channel.writeAndFlush(response);
            }
        } else if (jsonElement.isJsonArray()) {
            this.channel.writeAndFlush(this.handleBatchRequest(jsonElement.getAsJsonArray().asList()));
        } else {
            this.channel.writeAndFlush(JsonRPCErrors.INVALID_REQUEST.createWithUnknownId(null));
        }
    }
 
    private JsonArray handleBatchRequest(List<JsonElement> batchRequests) {
        JsonArray batchResponses = new JsonArray();
        batchRequests.stream().map(batchEntry -> this.handleJsonObject(batchEntry.getAsJsonObject())).filter(Objects::nonNull).forEach(batchResponses::add);
        return batchResponses;
    }
 
    public void sendNotification(Holder.Reference<? extends OutgoingRpcMethod<Void, ?>> method) {
        this.sendRequest(method, null, false);
    }
 
    public <Params> void sendNotification(Holder.Reference<? extends OutgoingRpcMethod<Params, ?>> method, Params params) {
        this.sendRequest(method, params, false);
    }
 
    public <Result> CompletableFuture<Result> sendRequest(Holder.Reference<? extends OutgoingRpcMethod<Void, Result>> method) {
        return this.sendRequest(method, null, true);
    }
 
    public <Params, Result> CompletableFuture<Result> sendRequest(Holder.Reference<? extends OutgoingRpcMethod<Params, Result>> method, Params params) {
        return this.sendRequest(method, params, true);
    }
 
    @Contract("_,_,false->null;_,_,true->!null")
    private <Params, Result> @Nullable CompletableFuture<Result> sendRequest(
        Holder.Reference<? extends OutgoingRpcMethod<Params, ? extends Result>> method, @Nullable Params params, boolean expectReply
    ) {
        List<JsonElement> jsonParams = params != null ? List.of(Objects.requireNonNull(method.value().encodeParams(params))) : List.of();
        if (expectReply) {
            CompletableFuture<Result> future = new CompletableFuture<>();
            int id = this.transactionId.incrementAndGet();
            long time = Util.timeSource.get(TimeUnit.MILLISECONDS);
            this.pendingRequests.put(id, new PendingRpcRequest<>(method, future, time + 5000L));
            this.channel.writeAndFlush(JsonRPCUtils.createRequest(id, method.key().identifier(), jsonParams));
            return future;
        } else {
            this.channel.writeAndFlush(JsonRPCUtils.createRequest(null, method.key().identifier(), jsonParams));
            return null;
        }
    }
 
    @VisibleForTesting
    @Nullable JsonObject handleJsonObject(JsonObject jsonObject) {
        try {
            JsonElement id = JsonRPCUtils.getRequestId(jsonObject);
            String method = JsonRPCUtils.getMethodName(jsonObject);
            JsonElement result = JsonRPCUtils.getResult(jsonObject);
            JsonElement params = JsonRPCUtils.getParams(jsonObject);
            JsonObject error = JsonRPCUtils.getError(jsonObject);
            if (method != null && result == null && error == null) {
                return id != null && !isValidRequestId(id)
                    ? JsonRPCErrors.INVALID_REQUEST.createWithUnknownId("Invalid request id - only String, Number and NULL supported")
                    : this.handleIncomingRequest(id, method, params);
            } else if (method == null && result != null && error == null && id != null) {
                if (isValidResponseId(id)) {
                    this.handleRequestResponse(id.getAsInt(), result);
                } else {
                    LOGGER.warn("Received respose {} with id {} we did not request", result, id);
                }
 
                return null;
            } else {
                return method == null && result == null && error != null
                    ? this.handleError(id, error)
                    : JsonRPCErrors.INVALID_REQUEST.createWithoutData(Objects.requireNonNullElse(id, JsonNull.INSTANCE));
            }
        } catch (Exception var7) {
            LOGGER.error("Error while handling rpc request", (Throwable)var7);
            return JsonRPCErrors.INTERNAL_ERROR.createWithUnknownId("Unknown error handling request - check server logs for stack trace");
        }
    }
 
    private static boolean isValidRequestId(JsonElement id) {
        return id.isJsonNull() || GsonHelper.isNumberValue(id) || GsonHelper.isStringValue(id);
    }
 
    private static boolean isValidResponseId(JsonElement id) {
        return GsonHelper.isNumberValue(id);
    }
 
    private @Nullable JsonObject handleIncomingRequest(@Nullable JsonElement id, String method, @Nullable JsonElement params) {
        boolean sendResponse = id != null;
 
        try {
            JsonElement result = this.dispatchIncomingRequest(method, params);
            return result != null && sendResponse ? JsonRPCUtils.createSuccessResult(id, result) : null;
        } catch (InvalidParameterJsonRpcException var6) {
            LOGGER.debug("Invalid parameter invocation {}: {}, {}", method, params, var6.getMessage());
            return sendResponse ? JsonRPCErrors.INVALID_PARAMS.create(id, var6.getMessage()) : null;
        } catch (EncodeJsonRpcException var7) {
            LOGGER.error("Failed to encode json rpc response {}: {}", method, var7.getMessage());
            return sendResponse ? JsonRPCErrors.INTERNAL_ERROR.create(id, var7.getMessage()) : null;
        } catch (InvalidRequestJsonRpcException var8) {
            return sendResponse ? JsonRPCErrors.INVALID_REQUEST.create(id, var8.getMessage()) : null;
        } catch (MethodNotFoundJsonRpcException var9) {
            return sendResponse ? JsonRPCErrors.METHOD_NOT_FOUND.create(id, var9.getMessage()) : null;
        } catch (Exception var10) {
            LOGGER.error("Error while dispatching rpc method {}", method, var10);
            return sendResponse ? JsonRPCErrors.INTERNAL_ERROR.createWithoutData(id) : null;
        }
    }
 
    public @Nullable JsonElement dispatchIncomingRequest(String method, @Nullable JsonElement params) {
        Identifier identifier = Identifier.tryParse(method);
        if (identifier == null) {
            throw new InvalidRequestJsonRpcException("Failed to parse method value: " + method);
        } else {
            Optional<IncomingRpcMethod<?, ?>> incomingRpcMethod = BuiltInRegistries.INCOMING_RPC_METHOD.getOptional(identifier);
            if (incomingRpcMethod.isEmpty()) {
                throw new MethodNotFoundJsonRpcException("Method not found: " + method);
            } else if (incomingRpcMethod.get().attributes().runOnMainThread()) {
                try {
                    return this.minecraftApi.<JsonElement>submit(() -> incomingRpcMethod.get().apply(this.minecraftApi, params, this.clientInfo)).join();
                } catch (CompletionException var8) {
                    if (var8.getCause() instanceof RuntimeException re) {
                        throw re;
                    } else {
                        throw var8;
                    }
                }
            } else {
                return incomingRpcMethod.get().apply(this.minecraftApi, params, this.clientInfo);
            }
        }
    }
 
    private void handleRequestResponse(int id, JsonElement result) {
        PendingRpcRequest<?> request = this.pendingRequests.remove(id);
        if (request == null) {
            LOGGER.warn("Received unknown response (id: {}): {}", id, result);
        } else {
            request.accept(result);
        }
    }
 
    private @Nullable JsonObject handleError(@Nullable JsonElement id, JsonObject error) {
        if (id != null && isValidResponseId(id)) {
            PendingRpcRequest<?> request = this.pendingRequests.remove(id.getAsInt());
            if (request != null) {
                request.resultFuture().completeExceptionally(new RemoteRpcErrorException(id, error));
            }
        }
 
        LOGGER.error("Received error (id: {}): {}", id, error);
        return null;
    }
}

引用的其他类

  • Channel

    • 引用位置: 参数/字段
  • Holder

    • 引用位置: 参数
  • RepeatedNarrator

    • 引用位置: 参数/返回值
  • Identifier

    • 引用位置: 方法调用
    • 关联成员: Identifier.tryParse()
  • JsonRPCUtils

    • 引用位置: 方法调用
    • 关联成员: JsonRPCUtils.createRequest(), JsonRPCUtils.createSuccessResult(), JsonRPCUtils.getError(), JsonRPCUtils.getMethodName(), JsonRPCUtils.getParams(), JsonRPCUtils.getRequestId(), JsonRPCUtils.getResult()
  • JsonRpcLogger

    • 引用位置: 参数/字段
  • ManagementServer

    • 引用位置: 参数/字段
  • OutgoingRpcMethod

    • 引用位置: 参数
  • PendingRpcRequest

    • 引用位置: 字段
  • MinecraftApi

    • 引用位置: 参数/字段
  • ClientInfo

    • 引用位置: 字段/方法调用
    • 关联成员: ClientInfo.of()
  • InvalidRequestJsonRpcException

    • 引用位置: 构造调用
    • 关联成员: InvalidRequestJsonRpcException()
  • MethodNotFoundJsonRpcException

    • 引用位置: 构造调用
    • 关联成员: MethodNotFoundJsonRpcException()
  • RemoteRpcErrorException

    • 引用位置: 构造调用
    • 关联成员: RemoteRpcErrorException()
  • GsonHelper

    • 引用位置: 方法调用
    • 关联成员: GsonHelper.isNumberValue(), GsonHelper.isStringValue()
  • Util

    • 引用位置: 方法调用
    • 关联成员: Util.getMillis()