Skip to content

Commit

Permalink
✨ Periodically query current players
Browse files Browse the repository at this point in the history
  • Loading branch information
MrGraversen committed Sep 23, 2023
1 parent c3d6b2a commit 4631e08
Show file tree
Hide file tree
Showing 13 changed files with 208 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/main/java/io/graversen/rust/rcon/DefaultRustRconService.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.graversen.rust.rcon.protocol.oxide.DefaultOxideManagement;
import io.graversen.rust.rcon.protocol.oxide.OxideManagement;
import io.graversen.rust.rcon.tasks.RconTask;
import io.graversen.rust.rcon.tasks.RustPlayersEmitTask;
import io.graversen.rust.rcon.tasks.ServerInfoEmitTask;
import io.graversen.rust.rcon.util.DefaultJsonMapper;
import io.graversen.rust.rcon.util.Lazy;
Expand All @@ -23,6 +24,7 @@
import javax.annotation.Nullable;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
Expand All @@ -37,6 +39,7 @@
public class DefaultRustRconService implements RustRconService {
private final AtomicBoolean isRconLogEnabled = new AtomicBoolean(false);
private final AtomicReference<RustDiagnostics> diagnostics = new AtomicReference<>(null);
private final AtomicReference<List<RustPlayer>> rustPlayers = new AtomicReference<>(List.of());

private final @NonNull RustRconConfiguration configuration;

Expand Down Expand Up @@ -99,6 +102,11 @@ public Optional<RustDiagnostics> diagnostics() {
return Optional.ofNullable(diagnostics.get());
}

@Override
public List<RustPlayer> rustPlayers() {
return rustPlayers.get();
}

@Override
public void registerEvents(@NonNull Object subscriber) {
eventBus.get().register(subscriber);
Expand All @@ -117,7 +125,14 @@ protected void configure() {
eventBus.get()::post
);
registerInternalTask(serverInfoEmitTask, Duration.ofSeconds(5));
final var rustPlayersEmitTask = new RustPlayersEmitTask(
rustRconClient.get().rustServer(),
() -> codec().admin().playerList().thenApply(rustDtoMappers.get().mapRustPlayers()),
eventBus.get()::post
);
registerInternalTask(rustPlayersEmitTask, Duration.ofSeconds(3));
registerRustDiagnosticsListener();
registerRustPlayerEventListener();
}

protected ScheduledExecutorService createScheduledExecutorService() {
Expand Down Expand Up @@ -199,6 +214,11 @@ private void registerRustDiagnosticsListener() {
registerEvents(new ServerInfoDiagnosticsEventListener(diagnostics::set));
}

private void registerRustPlayerEventListener() {
log.info("Registering {}", RustPlayerEventListener.class.getSimpleName());
registerEvents(new RustPlayerEventListener(rustPlayers::set));
}

private void runConfigure() {
log.info("Running internal configuration hook");
configure();
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/io/graversen/rust/rcon/RustPlayer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.graversen.rust.rcon;

import io.graversen.rust.rcon.protocol.util.PlayerName;
import io.graversen.rust.rcon.protocol.util.SteamId64;
import io.graversen.rust.rcon.util.CommonUtils;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Value;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.ZonedDateTime;

@Value
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class RustPlayer {
@NonNull SteamId64 steamId;
@NonNull PlayerName playerName;
@NonNull String ping;
@NonNull String ipAddress;
@NonNull Duration connectedDuration;
@NonNull BigDecimal health;

public ZonedDateTime connectedAt() {
return CommonUtils.now().minus(connectedDuration);
}
}
47 changes: 47 additions & 0 deletions src/main/java/io/graversen/rust/rcon/RustPlayerEventListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.graversen.rust.rcon;

import com.google.common.eventbus.Subscribe;
import io.graversen.rust.rcon.event.server.RustPlayersEvent;
import io.graversen.rust.rcon.protocol.dto.RustPlayerDTO;
import io.graversen.rust.rcon.protocol.util.PlayerName;
import io.graversen.rust.rcon.protocol.util.SteamId64;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;

@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public class RustPlayerEventListener {
private final @NonNull Consumer<List<RustPlayer>> rustPlayersConsumer;

@Subscribe
public void onServerInfo(RustPlayersEvent rustPlayersEvent) {
final var rustPlayers = mapRustPlayers().apply(rustPlayersEvent);
rustPlayersConsumer.accept(rustPlayers);
}

Function<RustPlayersEvent, List<RustPlayer>> mapRustPlayers() {
return rustPlayersEvent -> {
final var rustPlayers = rustPlayersEvent.getRustPlayers();
return rustPlayers.stream()
.map(mapRustPlayer())
.toList();
};
}

Function<RustPlayerDTO, RustPlayer> mapRustPlayer() {
return rustPlayerDTO -> new RustPlayer(
SteamId64.parseOrFail(rustPlayerDTO.getSteamId()),
new PlayerName(rustPlayerDTO.getPlayerName()),
rustPlayerDTO.getPing(),
rustPlayerDTO.getIpAddress().split(":")[0],
Duration.ofSeconds(rustPlayerDTO.getConnectedSeconds()),
BigDecimal.valueOf(rustPlayerDTO.getHealth())
);
}
}
3 changes: 3 additions & 0 deletions src/main/java/io/graversen/rust/rcon/RustRconService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import javax.annotation.Nullable;
import java.time.Duration;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

Expand All @@ -26,4 +27,6 @@ public interface RustRconService extends EventEmitter {
void schedule(@NonNull RconTask task, @NonNull Duration fixedDelay, @Nullable Duration initialDelay);

Optional<RustDiagnostics> diagnostics();

List<RustPlayer> rustPlayers();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.graversen.rust.rcon.event.server;

import io.graversen.rust.rcon.RustServer;
import io.graversen.rust.rcon.protocol.dto.RustPlayerDTO;
import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;

import java.util.List;

@Getter
@ToString(callSuper = true)
public class RustPlayersEvent extends ServerEvent {
private final @NonNull List<RustPlayerDTO> rustPlayers;

public RustPlayersEvent(@NonNull RustServer server, @NonNull List<RustPlayerDTO> rustPlayers) {
super(server);
this.rustPlayers = rustPlayers;
}
}
2 changes: 2 additions & 0 deletions src/main/java/io/graversen/rust/rcon/protocol/AdminCodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ public interface AdminCodec {
CompletableFuture<RustRconResponse> unmutePlayer(@NonNull SteamId64 steamId64);

CompletableFuture<RustRconResponse> serverInfo();

CompletableFuture<RustRconResponse> playerList();
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,10 @@ public CompletableFuture<RustRconResponse> serverInfo() {
final var rconMessage = compile(SERVER_INFO);
return send(rconMessage);
}

@Override
public CompletableFuture<RustRconResponse> playerList() {
final var rconMessage = compile(PLAYER_LIST);
return send(rconMessage);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public static class AdminProtocol {
public static final String MUTE_PLAYER = "global.mute \"" + STEAM_ID_64 + "\"";
public static final String UNMUTE_PLAYER = "global.unmute \"" + STEAM_ID_64 + "\"";
public static final String SERVER_INFO = "global.serverinfo";
public static final String PLAYER_LIST = "playerlist";
}

public static class OxideProtocol {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ public Function<RustRconResponse, BuildInfoDTO> mapBuildInfo() {
public Function<RustRconResponse, ServerInfoDTO> mapServerInfo() {
return rconResponse -> jsonMapper.fromJson(rconResponse.getMessage(), ServerInfoDTO.class);
}

public Function<RustRconResponse, List<RustPlayerDTO>> mapRustPlayers() {
return rconResponse -> jsonMapper.fromJsonArray(rconResponse.getMessage(), RustPlayerDTO.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.graversen.rust.rcon.protocol.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@AllArgsConstructor
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
public class RustPlayerDTO {
@JsonProperty("SteamID")
private final String steamId;

@JsonProperty("DisplayName")
private final String playerName;

@JsonProperty("Ping")
private final String ping;

@JsonProperty("Address")
private final String ipAddress;

@JsonProperty("ConnectedSeconds")
private final Integer connectedSeconds;

@JsonProperty("Health")
private final Double health;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.graversen.rust.rcon.tasks;

import io.graversen.rust.rcon.RustServer;
import io.graversen.rust.rcon.event.server.RustPlayersEvent;
import io.graversen.rust.rcon.protocol.dto.RustPlayerDTO;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

@Slf4j
@RequiredArgsConstructor
public class RustPlayersEmitTask implements RconTask {
private final @NonNull RustServer server;
private final @NonNull Supplier<CompletableFuture<List<RustPlayerDTO>>> rustPlayersGetter;
private final @NonNull Consumer<RustPlayersEvent> rustPlayersEventEmitter;

@Override
public void run() {
rustPlayersGetter.get()
.thenApply(rustPlayersEventMapper())
.thenAccept(rustPlayersEventEmitter);
}

Function<List<RustPlayerDTO>, RustPlayersEvent> rustPlayersEventMapper() {
return rustPlayers -> new RustPlayersEvent(server, rustPlayers);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import lombok.SneakyThrows;
import lombok.Synchronized;

import java.util.List;

public class DefaultJsonMapper implements JsonMapper {
private ObjectMapper objectMapper;

Expand All @@ -21,6 +23,13 @@ public <T> T fromJson(@NonNull String json, @NonNull Class<T> toClass) {
return getObjectMapper().readValue(json, toClass);
}

@Override
@SneakyThrows
public <T> List<T> fromJsonArray(@NonNull String json, @NonNull Class<T> toClass) {
final T[] jsonArray = (T[]) getObjectMapper().readValue(json, toClass.arrayType());
return List.of(jsonArray);
}

@Synchronized
private ObjectMapper getObjectMapper() {
if (objectMapper == null) {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/io/graversen/rust/rcon/util/JsonMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

import lombok.NonNull;

import java.util.List;

public interface JsonMapper {
String toJson(@NonNull Object object);

<T> T fromJson(@NonNull String json, @NonNull Class<T> toClass);

<T> List<T> fromJsonArray(@NonNull String json, @NonNull Class<T> toClass);
}

0 comments on commit 4631e08

Please sign in to comment.