minecraft-src/net/minecraft/client/telemetry/ClientTelemetryManager.java
2025-07-04 01:41:11 +03:00

104 lines
4.6 KiB
Java

package net.minecraft.client.telemetry;
import com.google.common.base.Suppliers;
import com.mojang.authlib.minecraft.TelemetrySession;
import com.mojang.authlib.minecraft.UserApiService;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.SharedConstants;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.User;
import org.jetbrains.annotations.Nullable;
@Environment(EnvType.CLIENT)
public class ClientTelemetryManager implements AutoCloseable {
private static final AtomicInteger THREAD_COUNT = new AtomicInteger(1);
private static final Executor EXECUTOR = Executors.newSingleThreadExecutor(runnable -> {
Thread thread = new Thread(runnable);
thread.setName("Telemetry-Sender-#" + THREAD_COUNT.getAndIncrement());
return thread;
});
private final Minecraft minecraft;
private final UserApiService userApiService;
private final TelemetryPropertyMap deviceSessionProperties;
private final Path logDirectory;
private final CompletableFuture<Optional<TelemetryLogManager>> logManager;
private final Supplier<TelemetryEventSender> outsideSessionSender = Suppliers.memoize(this::createEventSender);
public ClientTelemetryManager(Minecraft minecraft, UserApiService userApiService, User user) {
this.minecraft = minecraft;
this.userApiService = userApiService;
TelemetryPropertyMap.Builder builder = TelemetryPropertyMap.builder();
user.getXuid().ifPresent(string -> builder.put(TelemetryProperty.USER_ID, string));
user.getClientId().ifPresent(string -> builder.put(TelemetryProperty.CLIENT_ID, string));
builder.put(TelemetryProperty.MINECRAFT_SESSION_ID, UUID.randomUUID());
builder.put(TelemetryProperty.GAME_VERSION, SharedConstants.getCurrentVersion().getId());
builder.put(TelemetryProperty.OPERATING_SYSTEM, Util.getPlatform().telemetryName());
builder.put(TelemetryProperty.PLATFORM, System.getProperty("os.name"));
builder.put(TelemetryProperty.CLIENT_MODDED, Minecraft.checkModStatus().shouldReportAsModified());
builder.putIfNotNull(TelemetryProperty.LAUNCHER_NAME, Minecraft.getLauncherBrand());
this.deviceSessionProperties = builder.build();
this.logDirectory = minecraft.gameDirectory.toPath().resolve("logs/telemetry");
this.logManager = TelemetryLogManager.open(this.logDirectory);
}
public WorldSessionTelemetryManager createWorldSessionManager(boolean newWorld, @Nullable Duration worldLoadDuration, @Nullable String minigameName) {
return new WorldSessionTelemetryManager(this.createEventSender(), newWorld, worldLoadDuration, minigameName);
}
public TelemetryEventSender getOutsideSessionSender() {
return (TelemetryEventSender)this.outsideSessionSender.get();
}
private TelemetryEventSender createEventSender() {
if (!this.minecraft.allowsTelemetry()) {
return TelemetryEventSender.DISABLED;
} else {
TelemetrySession telemetrySession = this.userApiService.newTelemetrySession(EXECUTOR);
if (!telemetrySession.isEnabled()) {
return TelemetryEventSender.DISABLED;
} else {
CompletableFuture<Optional<TelemetryEventLogger>> completableFuture = this.logManager
.thenCompose(
optional -> (CompletionStage)optional.map(TelemetryLogManager::openLogger).orElseGet(() -> CompletableFuture.completedFuture(Optional.empty()))
);
return (telemetryEventType, consumer) -> {
if (!telemetryEventType.isOptIn() || Minecraft.getInstance().telemetryOptInExtra()) {
TelemetryPropertyMap.Builder builder = TelemetryPropertyMap.builder();
builder.putAll(this.deviceSessionProperties);
builder.put(TelemetryProperty.EVENT_TIMESTAMP_UTC, Instant.now());
builder.put(TelemetryProperty.OPT_IN, telemetryEventType.isOptIn());
consumer.accept(builder);
TelemetryEventInstance telemetryEventInstance = new TelemetryEventInstance(telemetryEventType, builder.build());
completableFuture.thenAccept(optional -> {
if (!optional.isEmpty()) {
((TelemetryEventLogger)optional.get()).log(telemetryEventInstance);
telemetryEventInstance.export(telemetrySession).send();
}
});
}
};
}
}
}
public Path getLogDirectory() {
return this.logDirectory;
}
public void close() {
this.logManager.thenAccept(optional -> optional.ifPresent(TelemetryLogManager::close));
}
}