1 | package com.example.pvptrust;
|
2 |
|
3 | import com.google.gson.Gson;
|
4 | import com.google.gson.GsonBuilder;
|
5 | import com.google.gson.JsonObject;
|
6 | import com.google.gson.JsonParser;
|
7 | import net.fabricmc.api.ModInitializer;
|
8 | import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
9 | import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
10 | import net.fabricmc.fabric.api.event.player.AttackEntityCallback;
|
11 | import net.minecraft.command.argument.EntityArgumentType;
|
12 | import com.mojang.brigadier.arguments.IntegerArgumentType;
|
13 | import net.minecraft.entity.player.PlayerEntity;
|
14 | import net.minecraft.server.command.CommandManager;
|
15 | import net.minecraft.server.network.ServerPlayerEntity;
|
16 | import net.minecraft.text.Text;
|
17 | import net.minecraft.util.ActionResult;
|
18 | import net.minecraft.entity.effect.StatusEffectInstance;
|
19 | import net.minecraft.entity.effect.StatusEffects;
|
20 |
|
21 | import java.io.*;
|
22 | import java.nio.file.Files;
|
23 | import java.nio.file.Path;
|
24 | import java.util.*;
|
25 | import java.util.concurrent.ConcurrentHashMap;
|
26 |
|
27 | import static net.minecraft.server.command.CommandManager.literal;
|
28 |
|
29 | public class PvPTrustMod implements ModInitializer {
|
30 | public static final String MOD_ID = "pvptrust";
|
31 | private static final Map<UUID, Set<UUID>> TRUST_MAP = new ConcurrentHashMap<>();
|
32 | private static final Map<UUID, Map<UUID, Long>> TEMP_TRUST_EXPIRATIONS = new ConcurrentHashMap<>();
|
33 | private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
34 | private static final Path DATA_FILE = Path.of("config", MOD_ID + "_data.json");
|
35 | private static final Path CONFIG_FILE = Path.of("config", MOD_ID + ".json");
|
36 |
|
37 | private int tickCounter = 0;
|
38 |
|
39 | private static Config config;
|
40 |
|
41 | public static class Config {
|
42 | public String attackMessage;
|
43 | public int trustBonusRadius;
|
44 | public int trustBonusDurationSeconds;
|
45 | public int trustBonusAmplifier;
|
46 |
|
47 | public Config() {
|
48 | this.attackMessage = "That player is trusted! You can't attack them until you /untrust them.";
|
49 | this.trustBonusRadius = 20;
|
50 | this.trustBonusDurationSeconds = 16;
|
51 | this.trustBonusAmplifier = 0;
|
52 | }
|
53 | }
|
54 |
|
55 | @Override
|
56 | public void onInitialize() {
|
57 | loadConfig();
|
58 | loadTrustData();
|
59 |
|
60 | CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
|
61 | dispatcher.register(literal("trust")
|
62 | .then(CommandManager.argument("target", EntityArgumentType.player())
|
63 | .executes(context -> {
|
64 | ServerPlayerEntity player = context.getSource().getPlayer();
|
65 | ServerPlayerEntity target = EntityArgumentType.getPlayer(context, "target");
|
66 | addTrust(player, target);
|
67 | return 1;
|
68 | })));
|
69 |
|
70 | dispatcher.register(literal("untrust")
|
71 | .then(CommandManager.argument("target", EntityArgumentType.player())
|
72 | .executes(context -> {
|
73 | ServerPlayerEntity player = context.getSource().getPlayer();
|
74 | ServerPlayerEntity target = EntityArgumentType.getPlayer(context, "target");
|
75 | removeTrust(player, target);
|
76 | return 1;
|
77 | })));
|
78 |
|
79 | dispatcher.register(literal("trustlist")
|
80 | .executes(context -> {
|
81 | ServerPlayerEntity player = context.getSource().getPlayer();
|
82 | Set<UUID> trusted = TRUST_MAP.getOrDefault(player.getUuid(), Collections.emptySet());
|
83 | if (trusted.isEmpty()) {
|
84 | player.sendMessage(Text.literal("You don't trust anyone."), false);
|
85 | } else {
|
86 | StringBuilder trustedNames = new StringBuilder("Trusted players: ");
|
87 | for (UUID uuid : trusted) {
|
88 | ServerPlayerEntity trustedPlayer = player.getServer().getPlayerManager().getPlayer(uuid);
|
89 | if (trustedPlayer != null) {
|
90 | trustedNames.append(trustedPlayer.getName().getString()).append(", ");
|
91 | }
|
92 | }
|
93 | if (trustedNames.length() > 18) {
|
94 | trustedNames.setLength(trustedNames.length() - 2);
|
95 | }
|
96 | player.sendMessage(Text.literal(trustedNames.toString()), false);
|
97 | }
|
98 | return 1;
|
99 | }));
|
100 |
|
101 | dispatcher.register(literal("trustfor")
|
102 | .then(CommandManager.argument("target", EntityArgumentType.player())
|
103 | .then(CommandManager.argument("seconds", IntegerArgumentType.integer(1))
|
104 | .executes(context -> {
|
105 | ServerPlayerEntity player = context.getSource().getPlayer();
|
106 | ServerPlayerEntity target = EntityArgumentType.getPlayer(context, "target");
|
107 | int seconds = IntegerArgumentType.getInteger(context, "seconds");
|
108 |
|
109 | addTrust(player, target);
|
110 | long expiryTime = System.currentTimeMillis() + (seconds * 1000L);
|
111 | TEMP_TRUST_EXPIRATIONS
|
112 | .computeIfAbsent(player.getUuid(), k -> new HashMap<>())
|
113 | .put(target.getUuid(), expiryTime);
|
114 |
|
115 | player.sendMessage(Text.literal("You temporarily trust " + target.getName().getString() + " for " + seconds + " seconds."), false);
|
116 | target.sendMessage(Text.literal(player.getName().getString() + " trusts you temporarily."), false);
|
117 | return 1;
|
118 | }))));
|
119 | });
|
120 |
|
121 | AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> {
|
122 | if (entity instanceof PlayerEntity target) {
|
123 | Set<UUID> trusted = TRUST_MAP.getOrDefault(player.getUuid(), Collections.emptySet());
|
124 | if (trusted.contains(target.getUuid())) {
|
125 | player.sendMessage(Text.literal(config.attackMessage), false);
|
126 | return ActionResult.FAIL;
|
127 | }
|
128 | }
|
129 | return ActionResult.PASS;
|
130 | });
|
131 |
|
132 | ServerTickEvents.START_SERVER_TICK.register(server -> {
|
133 | long currentTime = System.currentTimeMillis();
|
134 | for (UUID playerId : new HashSet<>(TEMP_TRUST_EXPIRATIONS.keySet())) {
|
135 | Map<UUID, Long> trustMap = TEMP_TRUST_EXPIRATIONS.get(playerId);
|
136 | if (trustMap == null) continue;
|
137 |
|
138 | for (UUID targetId : new HashSet<>(trustMap.keySet())) {
|
139 | if (trustMap.get(targetId) <= currentTime) {
|
140 | trustMap.remove(targetId);
|
141 | TRUST_MAP.getOrDefault(playerId, new HashSet<>()).remove(targetId);
|
142 |
|
143 | ServerPlayerEntity player = server.getPlayerManager().getPlayer(playerId);
|
144 | ServerPlayerEntity target = server.getPlayerManager().getPlayer(targetId);
|
145 | if (player != null) {
|
146 | player.sendMessage(Text.literal("Temporary trust for " + (target != null ? target.getName().getString() : targetId.toString()) + " has expired."), false);
|
147 | }
|
148 | if (target != null && player != null) {
|
149 | target.sendMessage(Text.literal(player.getName().getString() + "'s temporary trust has expired."), false);
|
150 | }
|
151 | }
|
152 | }
|
153 |
|
154 | if (trustMap.isEmpty()) {
|
155 | TEMP_TRUST_EXPIRATIONS.remove(playerId);
|
156 | }
|
157 | }
|
158 | });
|
159 |
|
160 | ServerTickEvents.START_SERVER_TICK.register(server -> {
|
161 | tickCounter++;
|
162 | if (tickCounter < 20) return;
|
163 | tickCounter = 0;
|
164 |
|
165 | int radius = config.trustBonusRadius;
|
166 | int durationTicks = config.trustBonusDurationSeconds * 20;
|
167 | int amplifier = config.trustBonusAmplifier;
|
168 |
|
169 | for (ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) {
|
170 | Set<UUID> trustedPlayers = TRUST_MAP.getOrDefault(player.getUuid(), Collections.emptySet());
|
171 | for (UUID trustedUUID : trustedPlayers) {
|
172 | ServerPlayerEntity trustedPlayer = server.getPlayerManager().getPlayer(trustedUUID);
|
173 | if (trustedPlayer != null) {
|
174 | Set<UUID> trustedOfTrusted = TRUST_MAP.getOrDefault(trustedUUID, Collections.emptySet());
|
175 | if (trustedOfTrusted.contains(player.getUuid())) {
|
176 | double distanceSq = player.squaredDistanceTo(trustedPlayer);
|
177 | if (distanceSq <= radius * radius) {
|
178 | StatusEffectInstance resistance = new StatusEffectInstance(StatusEffects.RESISTANCE, durationTicks, amplifier, false, true, false);
|
179 | if (!player.hasStatusEffect(StatusEffects.RESISTANCE)) {
|
180 | player.addStatusEffect(resistance);
|
181 | }
|
182 | if (!trustedPlayer.hasStatusEffect(StatusEffects.RESISTANCE)) {
|
183 | trustedPlayer.addStatusEffect(resistance);
|
184 | }
|
185 | }
|
186 | }
|
187 | }
|
188 | }
|
189 | }
|
190 | });
|
191 | }
|
192 |
|
193 | private void addTrust(ServerPlayerEntity player, ServerPlayerEntity target) {
|
194 | if (target == null) {
|
195 | player.sendMessage(Text.literal("Player not found."), false);
|
196 | return;
|
197 | } else if (player.getUuid().equals(target.getUuid())) {
|
198 | player.sendMessage(Text.literal("You can't trust yourself."), false);
|
199 | return;
|
200 | }
|
201 | TRUST_MAP.computeIfAbsent(player.getUuid(), k -> new HashSet<>()).add(target.getUuid());
|
202 | target.sendMessage(Text.literal(player.getName().getString() + " now trusts you."), false);
|
203 | saveTrustData();
|
204 | }
|
205 |
|
206 | private void removeTrust(ServerPlayerEntity player, ServerPlayerEntity target) {
|
207 | if (target == null) {
|
208 | player.sendMessage(Text.literal("Player not found."), false);
|
209 | return;
|
210 | } else if (player.getUuid().equals(target.getUuid())) {
|
211 | player.sendMessage(Text.literal("You can't untrust yourself."), false);
|
212 | return;
|
213 | }
|
214 | boolean removed = TRUST_MAP.computeIfPresent(player.getUuid(), (uuid, trustedSet) -> {
|
215 | trustedSet.remove(target.getUuid());
|
216 | return trustedSet.isEmpty() ? null : trustedSet;
|
217 | }) != null;
|
218 | if (removed) {
|
219 | target.sendMessage(Text.literal(player.getName().getString() + " no longer trusts you."), false);
|
220 | saveTrustData();
|
221 | }
|
222 | }
|
223 |
|
224 | private void loadTrustData() {
|
225 | try {
|
226 | if (Files.exists(DATA_FILE)) {
|
227 | try (Reader reader = Files.newBufferedReader(DATA_FILE)) {
|
228 | JsonObject json = JsonParser.parseReader(reader).getAsJsonObject();
|
229 | for (String key : json.keySet()) {
|
230 | UUID playerId = UUID.fromString(key);
|
231 | Set<UUID> trustedPlayers = new HashSet<>();
|
232 | for (var element : json.getAsJsonArray(key)) {
|
233 | trustedPlayers.add(UUID.fromString(element.getAsString()));
|
234 | }
|
235 | TRUST_MAP.put(playerId, trustedPlayers);
|
236 | }
|
237 | }
|
238 | }
|
239 | } catch (IOException e) {
|
240 | System.err.println("Failed to load trust data: " + e.getMessage());
|
241 | e.printStackTrace();
|
242 | }
|
243 | }
|
244 |
|
245 | private void saveTrustData() {
|
246 | try {
|
247 | JsonObject json = new JsonObject();
|
248 | for (Map.Entry<UUID, Set<UUID>> entry : TRUST_MAP.entrySet()) {
|
249 | json.add(entry.getKey().toString(), GSON.toJsonTree(entry.getValue()));
|
250 | }
|
251 | Files.createDirectories(DATA_FILE.getParent());
|
252 | try (Writer writer = Files.newBufferedWriter(DATA_FILE)) {
|
253 | GSON.toJson(json, writer);
|
254 | }
|
255 | } catch (IOException e) {
|
256 | System.err.println("Failed to save trust data: " + e.getMessage());
|
257 | e.printStackTrace();
|
258 | }
|
259 | }
|
260 |
|
261 | private void loadConfig() {
|
262 | try {
|
263 | if (Files.exists(CONFIG_FILE)) {
|
264 | try (Reader reader = Files.newBufferedReader(CONFIG_FILE)) {
|
265 | config = GSON.fromJson(reader, Config.class);
|
266 | }
|
267 | } else {
|
268 | config = new Config(); // Default values are set in the constructor
|
269 | saveConfig();
|
270 | }
|
271 | } catch (IOException e) {
|
272 | System.err.println("Failed to load config: " + e.getMessage());
|
273 | e.printStackTrace();
|
274 | config = new Config(); // Fallback to default config
|
275 | }
|
276 | }
|
277 |
|
278 | private void saveConfig() {
|
279 | try {
|
280 | Files.createDirectories(CONFIG_FILE.getParent());
|
281 | try (Writer writer = Files.newBufferedWriter(CONFIG_FILE)) {
|
282 | GSON.toJson(config, writer);
|
283 | }
|
284 | } catch (IOException e) {
|
285 | System.err.println("Failed to save config: " + e.getMessage());
|
286 | e.printStackTrace();
|
287 | }
|
288 | }
|
289 | }
|