com.ibasco.agql.protocols.valve.source.query.SourceQueryClient Maven / Gradle / Ivy
/*
* Copyright (c) 2022 Asynchronous Game Query Library
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ibasco.agql.protocols.valve.source.query;
import com.ibasco.agql.core.NettyMessenger;
import com.ibasco.agql.core.NettySocketClient;
import com.ibasco.agql.core.util.OptionBuilder;
import com.ibasco.agql.core.util.Options;
import com.ibasco.agql.protocols.valve.source.query.challenge.SourceQueryChallengeRequest;
import com.ibasco.agql.protocols.valve.source.query.challenge.SourceQueryChallengeResponse;
import com.ibasco.agql.protocols.valve.source.query.common.enums.SourceChallengeType;
import com.ibasco.agql.protocols.valve.source.query.common.exceptions.SourceChallengeException;
import com.ibasco.agql.protocols.valve.source.query.common.message.SourceQueryRequest;
import com.ibasco.agql.protocols.valve.source.query.common.message.SourceQueryResponse;
import com.ibasco.agql.protocols.valve.source.query.info.SourceQueryInfoRequest;
import com.ibasco.agql.protocols.valve.source.query.info.SourceQueryInfoResponse;
import com.ibasco.agql.protocols.valve.source.query.players.SourceQueryPlayerRequest;
import com.ibasco.agql.protocols.valve.source.query.players.SourceQueryPlayerResponse;
import com.ibasco.agql.protocols.valve.source.query.rules.SourceQueryRulesRequest;
import com.ibasco.agql.protocols.valve.source.query.rules.SourceQueryRulesResponse;
import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture;
/**
*
* A query client for retreiving information on Source servers. Based on the Valve Source Server Query Protocol
*
*
* Example:
*
* Asynchronous (non-blocking)
*
*
* try (SourceQueryClient client = new SourceQueryClient()) {
* CompletableFuture<Source> resultFuture = client.getInfo(new InetSocketAddress("192.168.1.10", 27015);
* if (resultFuture.isDone()) {
* SourceQueryInfoResponse infoResponse = resultFuture.getNow(null);
* assert infoResponse != null;
* System.out.printf("INFO: %s\n", infoResponse.getResult());
* } else {
* //we have not yet received a response, register a callback once we do
* resultFuture.whenComplete((response, error) -> {
* if (error != null) {
* error.printStackTrace(System.err);
* return;
* }
* assert response != null;
* System.out.printf("INFO: %s\n", response);
* });
* }
* }
*
*
* Synchronous (blocking)
*
*
* try (SourceQueryClient client = new SourceQueryClient()) {
* SourceQueryInfoResponse infoResponse = client.getInfo(new InetSocketAddress("192.168.1.10", 27015);
* assert infoResponse != null;
* System.out.printf("INFO: %s\n", infoResponse.getResult());
* } catch (Exception error) {
* error.printStackTrace(System.err);
* }
*
*
* Modifying client configuration parameters
*
* Enable rate-limiting (disabled by default)
*
*
* SourceQueryOptions options = SourceQueryOptions.builder().option(SourceQueryOptions.FAILSAFE_RATELIMIT_ENABLED, true)
* .option(SourceQueryOptions.FAILSAFE_RATELIMIT_TYPE, RateLimitType.SMOOTH)
* .build();
*
* //pass options to client's constructor
* try (SourceQueryClient client = new SourceQueryClient(options)) {
* InetSocketAddress address = new InetSocketAddress("192.168.1.10", 27015);
*
* //execute query 100 times
* for (int i=0; i<100; i++) {
* CompletableFuture<Source> resultFuture = client.getInfo(address);
* if (resultFuture.isDone()) {
* SourceQueryInfoResponse infoResponse = resultFuture.getNow(null);
* assert infoResponse != null;
* System.out.printf("INFO: %s\n", infoResponse.getResult());
* } else {
* //we have not yet received a response, register a callback once we do
* resultFuture.whenComplete((response, error) -> {
* if (error != null) {
* error.printStackTrace(System.err);
* return;
* }
* assert response != null;
* System.out.printf("INFO: %s\n", response);
* });
* }
* }
* }
*
*
* @author Rafael Luis Ibasco
* @see Valve Source Server Query Protocol
* @see SourceQueryOptions
*/
public final class SourceQueryClient extends NettySocketClient> {
//
/**
* Create a new {@link com.ibasco.agql.protocols.valve.source.query.SourceQueryClient} instance using the pre-defined configuration {@link com.ibasco.agql.core.util.Options} for this client
*/
public SourceQueryClient() {
this(null);
}
/**
* Create a new {@link com.ibasco.agql.protocols.valve.source.query.SourceQueryClient} instance using the provided {@link SourceQueryOptions}
*
* @param options
* A {@link SourceQueryOptions} object containing all the user-defined configuration values to be used by the client.
*
* @see Options
* @see OptionBuilder
*/
public SourceQueryClient(SourceQueryOptions options) {
super(options);
}
//
/**
* Retrieves information about the Source server. A challengenumber will automatically be obtained if required by the server.
*
* @param address
* The {@link java.net.InetSocketAddress} containing the ip address and port number information of the target server
*
* @return A {@link java.util.concurrent.CompletableFuture} that is notified once a response has been received from the server. If successful, the {@link java.util.concurrent.CompletableFuture} returns a value of {@link com.ibasco.agql.protocols.valve.source.query.info.SourceQueryInfoResponse} which provides additional details on the server.
*
* @see #getInfo(InetSocketAddress, Integer)
*/
public CompletableFuture getInfo(InetSocketAddress address) {
return getInfo(address, null);
}
/**
* Retrieves information about the Source server using the provided challenge number (optional)
*
* @param address
* The {@link java.net.InetSocketAddress} containing the IP address and port number information of the target server
* @param challenge
* (optional) A 32-bit signed integer anti-spoofing challenge. Set to {@code null} to let the library obtain one automatically. Passing a non-null integer will implicitly disable auto-update. This means that the library will not automatically send a new challenge request if the server requires a new one. Instead, it will throw a {@link SourceChallengeException} where a valid challenge number can be obtained via {@link SourceChallengeException#getChallenge()}. This is similar to calling {@link #getInfo(InetSocketAddress)}.
*
* @return A {@link java.util.concurrent.CompletableFuture} that is notified once a response has been received from the server. If successful, the {@link java.util.concurrent.CompletableFuture} returns a value of {@link com.ibasco.agql.protocols.valve.source.query.info.SourceQueryInfoResponse} which provides additional details on the server
*
* @throws SourceChallengeException
* If auto-update is disabled (challenge argument is {@code null}) and the current challenge number has been invalidated.
* @see Changes to A2S_INFO protocol
* @see Steam Client Updates as of 12/08/2020
*/
public CompletableFuture getInfo(InetSocketAddress address, Integer challenge) {
return send(address, new SourceQueryInfoRequest(challenge), SourceQueryInfoResponse.class);
}
/**
* Retrieve rules from the Source server
*
* @param address
* The {@link java.net.InetSocketAddress} containing the IP address and port number information of the target server.
*
* @return A {@link java.util.concurrent.CompletableFuture} that is notified once a response has been received from the server. If successful, the {@link java.util.concurrent.CompletableFuture} returns a value of {@link com.ibasco.agql.protocols.valve.source.query.rules.SourceQueryRulesResponse} which provides additional details on the response.
*
* @see #getRules(InetSocketAddress, Integer)
* @see #getChallenge(InetSocketAddress, SourceChallengeType)
*/
public CompletableFuture getRules(InetSocketAddress address) {
return getRules(address, null);
}
/**
* Retrieve rules from the Source server
*
*
* Note: This method requires a valid challenge number which can be obtained via {@link #getChallenge(InetSocketAddress, SourceChallengeType)}
*
*
* @param address
* The {@link java.net.InetSocketAddress} containing the IP address and port number information of the target server
* @param challenge
* (optional) A 32-bit signed integer anti-spoofing challenge. Set to {@code null} to let the library obtain one automatically. Passing a non-null integer will implicitly disable auto-update. This means that the library will not automatically send a new challenge request if the server requires a new one. Instead, it will throw a {@link SourceChallengeException} where a valid challenge number can be obtained via {@link SourceChallengeException#getChallenge()}. This is similar to calling {@link #getRules(InetSocketAddress)}.
*
* @return A {@link java.util.concurrent.CompletableFuture} that is notified once a response has been received from the server. If successful, the future returns a value of {@link com.ibasco.agql.protocols.valve.source.query.rules.SourceQueryRulesResponse} which provides additional details on the response.
*
* @throws SourceChallengeException
* If auto-update is disabled (challenge argument is {@code null}) and the current challenge number has been invalidated.
* @see #getRules(InetSocketAddress, Integer)
*/
public CompletableFuture getRules(InetSocketAddress address, Integer challenge) {
return send(address, new SourceQueryRulesRequest(challenge), SourceQueryRulesResponse.class);
}
/**
*
* Retrieve a list of active players in the server.
*
*
* @param address
* The {@link java.net.InetSocketAddress} containing the IP address and port number information of the target server
*
* @return A {@link java.util.concurrent.CompletableFuture} that contains a {@link java.util.List} of {@link com.ibasco.agql.protocols.valve.source.query.players.SourcePlayer} currently residing on
* the server
*
* @see #getPlayers(InetSocketAddress, Integer)
*/
public CompletableFuture getPlayers(InetSocketAddress address) {
return send(address, new SourceQueryPlayerRequest(), SourceQueryPlayerResponse.class);
}
/**
* Retrieve a list of active players in the server. You need to obtain a valid challenge number from the server
* first via {@link #getChallenge(InetSocketAddress, SourceChallengeType)}
*
* @param address
* The {@link java.net.InetSocketAddress} containing the IP address and port number information of the target server
* @param challenge
* (optional) A 32-bit signed integer anti-spoofing challenge. Set to {@code null} to let the library obtain one automatically. Passing a non-null integer will implicitly disable auto-update. This means that the library will not automatically send a new challenge request if the server requires a new one. Instead, it will throw a {@link SourceChallengeException} where a valid challenge number can be obtained via {@link SourceChallengeException#getChallenge()}. This is similar to calling {@link #getPlayers(InetSocketAddress)}.
*
* @return A {@link java.util.concurrent.CompletableFuture} that contains a {@link java.util.List} of {@link com.ibasco.agql.protocols.valve.source.query.players.SourcePlayer} currently residing on
* the server
*
* @throws SourceChallengeException
* If auto-update is disabled (challenge argument is {@code null}) and the current challenge number has been invalidated.
* @see #getPlayers(InetSocketAddress)
* @see #getChallenge(InetSocketAddress, SourceChallengeType)
*/
public CompletableFuture getPlayers(InetSocketAddress address, Integer challenge) {
return send(address, new SourceQueryPlayerRequest(challenge), SourceQueryPlayerResponse.class);
}
/**
* Obtains a 4-byte (32-bit) anti-spoofing integer from the server. This is used for queries (such as PLAYERS, RULES or INFO) that requires a challenge number.
*
* @param address
* The {@link java.net.InetSocketAddress} containing the IP address and port number information of the target server
* @param type
* The {@link com.ibasco.agql.protocols.valve.source.query.common.enums.SourceChallengeType} enumeration which identifies the type of server challenge.
*
* @return A {@link java.util.concurrent.CompletableFuture} returning a value of {@link java.lang.Integer} representing the server challenge number
*/
public CompletableFuture getChallenge(InetSocketAddress address, SourceChallengeType type) {
return send(address, new SourceQueryChallengeRequest(type), SourceQueryChallengeResponse.class);
}
/** {@inheritDoc} */
@Override
protected NettyMessenger> createMessenger(Options options) {
return new SourceQueryMessenger(options);
}
}