Skip to content

Commit

Permalink
feat(core): core feature implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
Yusuf007R committed Aug 23, 2024
1 parent 9390265 commit 2e8c84e
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 166 deletions.
14 changes: 1 addition & 13 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,10 @@ repositories {
// for more information about repositories.
}

loom {
splitEnvironmentSourceSets()

mods {
"statssync" {
sourceSet sourceSets.main
sourceSet sourceSets.client
}
}

}

dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
mappings loom.officialMojangMappings()
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"

// Fabric API. This is technically optional, but you probably want it anyway.
Expand Down
10 changes: 0 additions & 10 deletions src/client/java/com/yusuf007r/statssync/StatsSyncClient.java

This file was deleted.

This file was deleted.

11 changes: 0 additions & 11 deletions src/client/resources/statssync.client.mixins.json

This file was deleted.

96 changes: 13 additions & 83 deletions src/main/java/com/yusuf007r/statssync/StatsSync.java
Original file line number Diff line number Diff line change
@@ -1,99 +1,29 @@
package com.yusuf007r.statssync;

import static net.minecraft.server.command.CommandManager.*;


import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.StringArgumentType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.minecraft.command.argument.ScoreboardCriterionArgumentType;
import net.minecraft.command.argument.ScoreboardObjectiveArgumentType;
import net.minecraft.scoreboard.*;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.stat.ServerStatHandler;
import net.minecraft.stat.Stat;
import net.minecraft.text.Text;
import net.minecraft.util.UserCache;
import net.minecraft.util.WorldSavePath;
import org.jetbrains.annotations.NotNull;
import net.minecraft.commands.arguments.ObjectiveArgument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
import static net.minecraft.commands.SharedSuggestionProvider.suggest;


public class StatsSync implements ModInitializer {
public static final String MOD_ID = "statssync";
public static final String MOD_ID = "stats-sync";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
public static final UserCacheManager userCacheManager = new UserCacheManager();

void updateObjective(MinecraftServer server, ScoreboardObjective objective, ScoreHolder scoreHolder) {
ScoreboardCriterion criterion = objective.getCriterion();
Optional<UUID> playerUUID = getPlayerUUID(server, scoreHolder);

if (playerUUID.isEmpty()) {
LOGGER.warn("Could not find UUID for player: {}", scoreHolder.getNameForScoreboard());
return;
}

Path statsPath = getStatsFilePath(server, playerUUID.get());
if (!statsPath.toFile().exists()) {
LOGGER.warn("Stats file not found for player: {}", scoreHolder.getNameForScoreboard());
return;
}

int statValue = getStatValue(server, statsPath, criterion);
if (statValue > 0) {
updateScore(server, objective, scoreHolder, statValue);
}
}

private Optional<UUID> getPlayerUUID(@NotNull MinecraftServer server, ScoreHolder scoreHolder) {
return Optional.ofNullable(server.getUserCache()).flatMap(cache -> cache.findByName(scoreHolder.getNameForScoreboard())).map(GameProfile::getId);
}

private @NotNull Path getStatsFilePath(@NotNull MinecraftServer server, UUID playerUUID) {
return server.getSavePath(WorldSavePath.STATS).resolve(playerUUID + ".json");
}

private int getStatValue(MinecraftServer server, @NotNull Path statsPath, ScoreboardCriterion criterion) {
ServerStatHandler statHandler = new ServerStatHandler(server, statsPath.toFile());
return statHandler.getStat((Stat<?>) criterion);
}

private void updateScore(@NotNull MinecraftServer server, ScoreboardObjective objective, ScoreHolder scoreHolder, int value) {
server.getScoreboard().getOrCreateScore(scoreHolder, objective).setScore(value);
}


@Override
public void onInitialize() {
LOGGER.info("Hello Fabric world!");

CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> dispatcher.register(literal("statSync").executes(context -> {
ServerCommandSource source = context.getSource();
MinecraftServer server = source.getServer();
ServerScoreboard scoreboard = server.getScoreboard();


source.sendFeedback(()-> Text.literal("Found " + scoreboard.getObjectives().size() + " Objectives, will try updating them with player stats"), false);
scoreboard.getObjectives().forEach((obj) -> {
try {
source.sendFeedback(()-> Text.literal("trying to update: " + obj.getDisplayName().getString()), false);
scoreboard.getKnownScoreHolders().forEach((scoreHolder -> {
updateObjective(server, obj, scoreHolder);
}));

} catch (Exception e) {
LOGGER.info("Error while updating {}, {} the error was {}", obj.getDisplayName().getString(), obj.getCriterion().getName(), e.getMessage());
source.sendFeedback(()-> Text.literal("Error while updating: " + obj.getDisplayName().getString()), false);
}
});
return 1;
})));
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> dispatcher.register(literal("statSync").executes((c) -> new StatsSyncCommand(c).executeAllObjectivesAllPlayers())
.then(argument("objective", new ObjectiveArgument()).executes(c -> new StatsSyncCommand(c).executeSpecificObjectiveAllPlayers())
.then(argument("player", StringArgumentType.word()).suggests((c, b) -> suggest(userCacheManager.getUserCache()
.stream().map(user -> user.profile().getName()), b))
.executes(c -> new StatsSyncCommand(c).executeSpecificObjectiveSpecificPlayer())))));
}
}
120 changes: 120 additions & 0 deletions src/main/java/com/yusuf007r/statssync/StatsSyncCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.yusuf007r.statssync;

import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.ObjectiveArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerScoreboard;
import net.minecraft.stats.ServerStatsCounter;
import net.minecraft.stats.Stat;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraft.world.scores.Objective;
import net.minecraft.world.scores.ScoreAccess;
import net.minecraft.world.scores.ScoreHolder;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;

import java.nio.file.Path;
import java.util.Optional;
import java.util.UUID;

public class StatsSyncCommand {

final private CommandSourceStack source;
final private MinecraftServer server;
final private ServerScoreboard scoreboard;
final private CommandContext<CommandSourceStack> context;
final private UserCacheManager userCacheManager = StatsSync.userCacheManager;
public static final Logger LOGGER = StatsSync.LOGGER;


StatsSyncCommand(CommandContext<CommandSourceStack> context) {
this.context = context;
this.source = context.getSource();
this.server = source.getServer();
this.scoreboard = server.getScoreboard();

}


public int executeAllObjectivesAllPlayers() {
source.sendSuccess(() -> Component.literal("Updating all objectives for all players"), false);
scoreboard.getObjectives().forEach(this::updateObjectiveForAllPlayers);

return 1;
}

public int executeSpecificObjectiveAllPlayers() throws CommandSyntaxException {
Objective objective = ObjectiveArgument.getObjective(context, "objective");
source.sendSuccess(() -> Component.literal("Updating objective " + objective.getDisplayName()
.getString() + " for all players"), false);
updateObjectiveForAllPlayers(objective);

return 1;
}

public int executeSpecificObjectiveSpecificPlayer() throws CommandSyntaxException {
Objective objective = ObjectiveArgument.getObjective(context, "objective");
String playerName = StringArgumentType.getString(context, "player");
Optional<UserCacheManager.Entry> entry = userCacheManager.getUserByName(playerName);
if (entry.isEmpty()) {
LOGGER.error("couldn't find user {} in the cache", playerName);
}
source.sendSuccess(() -> Component.literal("Updating objective " + objective.getDisplayName()
.getString() + " for player " + entry.orElseThrow().profile().getName()), false);
updateObjective(objective, entry.orElseThrow().profile());

return 1;
}


private void updateObjectiveForAllPlayers(Objective objective) {
try {
source.sendSuccess(() -> Component.literal("Updating: " + objective.getDisplayName().getString()), false);
for (UserCacheManager.Entry entry : userCacheManager.getUserCache()) {
updateObjective(objective, entry.profile());
}
} catch (Exception e) {
LOGGER.error("Error while updating {}: {}", objective.getDisplayName().getString(), e.getMessage());
source.sendSuccess(() -> Component.literal("Error while updating: " + objective.getDisplayName()
.getString()), false);
}
}

void updateObjective(@NotNull Objective objective, GameProfile profile) {
ObjectiveCriteria criterion = objective.getCriteria();
UUID playerUUID = profile.getId();

Path statsPath = getStatsFilePath(playerUUID);
if (!statsPath.toFile().exists()) {
LOGGER.warn("Stats file not found for player: {}", profile.getName());
return;
}

int statValue = getStatValue(statsPath, criterion);
if (statValue > 0) {
updateScore(objective, profile, statValue);
}
}


private @NotNull Path getStatsFilePath(UUID playerUUID) {
return server.getWorldPath(LevelResource.PLAYER_STATS_DIR).resolve(playerUUID + ".json");
}

private int getStatValue(@NotNull Path statsPath, ObjectiveCriteria criterion) {
ServerStatsCounter statHandler = new ServerStatsCounter(server, statsPath.toFile());
return statHandler.getValue((Stat<?>) criterion);
}

private void updateScore(Objective objective, GameProfile profile, int value) {
ScoreAccess scoreAccess = scoreboard.getOrCreatePlayerScore(ScoreHolder.fromGameProfile(profile), objective);
scoreAccess.set(value);
}

}
Loading

0 comments on commit 2e8c84e

Please sign in to comment.