diff --git a/build.gradle b/build.gradle index 27c50be8..56cd1324 100644 --- a/build.gradle +++ b/build.gradle @@ -28,6 +28,7 @@ dependencies { modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modImplementation "me.lucko:fabric-permissions-api:${project.lucko_permissions_version}" + compileOnly "net.luckperms:api:${project.lpapi_version}" modImplementation "eu.pb4:placeholder-api:${project.papi_version}" compileOnly "us.dynmap:DynmapCoreAPI:${project.dynmap_api_version}" } diff --git a/gradle.properties b/gradle.properties index fe511fb0..412d471b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,5 +13,6 @@ archives_base_name = factions # Dependencies fabric_version=0.58.5+1.19.1 lucko_permissions_version=0.1-SNAPSHOT +lpapi_version = 5.4 dynmap_api_version=3.4-beta-3 papi_version=2.0.0-beta.7+1.19 \ No newline at end of file diff --git a/src/main/java/io/icker/factions/api/events/PlayerEvents.java b/src/main/java/io/icker/factions/api/events/PlayerEvents.java index e6b36e64..d882cca5 100644 --- a/src/main/java/io/icker/factions/api/events/PlayerEvents.java +++ b/src/main/java/io/icker/factions/api/events/PlayerEvents.java @@ -50,11 +50,11 @@ public class PlayerEvents { }); /** - * Called when a player is killed by another player + * Called when a player dies */ - public static final Event ON_KILLED_BY_PLAYER = EventFactory.createArrayBacked(KilledByPlayer.class, callbacks -> (player, source) -> { - for (KilledByPlayer callback : callbacks) { - callback.onKilledByPlayer(player, source); + public static final Event ON_PLAYER_DEATH = EventFactory.createArrayBacked(PlayerDeath.class, callbacks -> (player, source, killedByPlayer) -> { + for (PlayerDeath callback : callbacks) { + callback.onPlayerDeath(player, source, killedByPlayer); } }); @@ -97,8 +97,8 @@ public interface Move { } @FunctionalInterface - public interface KilledByPlayer { - void onKilledByPlayer(ServerPlayerEntity player, DamageSource source); + public interface PlayerDeath { + void onPlayerDeath(ServerPlayerEntity player, DamageSource source, boolean killedByPlayer); } @FunctionalInterface diff --git a/src/main/java/io/icker/factions/api/persistents/Faction.java b/src/main/java/io/icker/factions/api/persistents/Faction.java index dac69b56..a2326482 100644 --- a/src/main/java/io/icker/factions/api/persistents/Faction.java +++ b/src/main/java/io/icker/factions/api/persistents/Faction.java @@ -7,12 +7,15 @@ import io.icker.factions.database.Name; import net.minecraft.inventory.SimpleInventory; import net.minecraft.util.Formatting; +import org.jetbrains.annotations.NotNull; import java.util.*; @Name("Faction") public class Faction { private static final HashMap STORE = Database.load(Faction.class, Faction::getID); + @Field("Invites") + public ArrayList invites = new ArrayList<>(); @Field("ID") private UUID id; @@ -35,37 +38,25 @@ public class Faction { @Field("Open") private boolean open; - @Field("Power") - private int power; - @Field("Home") private Home home; @Field("Safe") private SimpleInventory safe = new SimpleInventory(54); - @Field("Invites") - public ArrayList invites = new ArrayList<>(); - @Field("Relationships") private ArrayList relationships = new ArrayList<>(); - public Faction(String name, String description, String motd, Formatting color, boolean open, int power) { + public Faction(String name, String description, String motd, Formatting color, boolean open) { this.id = UUID.randomUUID(); this.name = name; this.motd = motd; this.description = description; this.color = color.getName(); this.open = open; - this.power = power; } - @SuppressWarnings("unused") - public Faction() {} - - @SuppressWarnings("unused") - public String getKey() { - return id.toString(); + public Faction() { } public static Faction get(UUID id) { @@ -74,10 +65,10 @@ public static Faction get(UUID id) { public static Faction getByName(String name) { return STORE.values() - .stream() - .filter(f -> f.name.equals(name)) - .findFirst() - .orElse(null); + .stream() + .filter(f -> f.name.equals(name)) + .findFirst() + .orElse(null); } public static void add(Faction faction) { @@ -91,9 +82,14 @@ public static Collection all() { @SuppressWarnings("unused") public static List allBut(UUID id) { return STORE.values() - .stream() - .filter(f -> f.id != id) - .toList(); + .stream() + .filter(f -> f.id != id) + .toList(); + } + + @SuppressWarnings("unused") + public String getKey() { + return id.toString(); } public UUID getID() { @@ -104,48 +100,53 @@ public String getName() { return name; } + public void setName(String name) { + this.name = name; + FactionEvents.MODIFY.invoker().onModify(this); + } + public Formatting getColor() { return Formatting.byName(color); } + public void setColor(Formatting color) { + this.color = color.getName(); + FactionEvents.MODIFY.invoker().onModify(this); + } + public String getDescription() { return description; } + public void setDescription(String description) { + this.description = description; + FactionEvents.MODIFY.invoker().onModify(this); + } + public String getMOTD() { return motd; } + public void setMOTD(String motd) { + this.motd = motd; + FactionEvents.MODIFY.invoker().onModify(this); + } + public int getPower() { - return power; + return getUsers().stream().mapToInt(User::getPower).sum(); } public SimpleInventory getSafe() { return safe; } - public boolean isOpen() { - return open; - } - - public void setName(String name) { - this.name = name; - FactionEvents.MODIFY.invoker().onModify(this); - } - - public void setDescription(String description) { - this.description = description; - FactionEvents.MODIFY.invoker().onModify(this); - } - - public void setMOTD(String motd) { - this.motd = motd; - FactionEvents.MODIFY.invoker().onModify(this); + @SuppressWarnings("unused") + public void setSafe(SimpleInventory safe) { + this.safe = safe; } - public void setColor(Formatting color) { - this.color = color.getName(); - FactionEvents.MODIFY.invoker().onModify(this); + public boolean isOpen() { + return open; } public void setOpen(boolean open) { @@ -153,18 +154,6 @@ public void setOpen(boolean open) { FactionEvents.MODIFY.invoker().onModify(this); } - public int adjustPower(int adjustment) { - int maxPower = calculateMaxPower(); - int newPower = Math.min(Math.max(0, power + adjustment), maxPower); - int oldPower = this.power; - - if (newPower == oldPower) return 0; - - power = newPower; - FactionEvents.POWER_CHANGE.invoker().onPowerChange(this, oldPower); - return Math.abs(newPower - oldPower); - } - public List getUsers() { return User.getByFaction(id); } @@ -175,8 +164,7 @@ public List getClaims() { public void removeAllClaims() { Claim.getByFaction(id) - .stream() - .forEach(Claim::remove); + .forEach(Claim::remove); FactionEvents.REMOVE_ALL_CLAIMS.invoker().onRemoveAllClaims(this); } @@ -208,7 +196,7 @@ public Relationship getReverse(Relationship rel) { public boolean isMutualAllies(UUID target) { Relationship rel = getRelationship(target); return rel.status == Relationship.Status.ALLY && getReverse(rel).status == Relationship.Status.ALLY; - } + } public List getMutualAllies() { return relationships.stream().filter(rel -> isMutualAllies(rel.target)).toList(); @@ -218,6 +206,7 @@ public List getEnemiesWith() { return relationships.stream().filter(rel -> rel.status == Relationship.Status.ENEMY).toList(); } + @SuppressWarnings("unused") //util public List getEnemiesOf() { return relationships.stream().filter(rel -> getReverse(rel).status == Relationship.Status.ENEMY).toList(); } @@ -226,7 +215,7 @@ public void removeRelationship(UUID target) { relationships = new ArrayList<>(relationships.stream().filter(rel -> !rel.target.equals(target)).toList()); } - public void setRelationship(Relationship relationship) { + public void setRelationship(@NotNull Relationship relationship) { if (getRelationship(relationship.target) != null) { removeRelationship(relationship.target); } @@ -246,6 +235,15 @@ public void remove() { FactionEvents.DISBAND.invoker().onDisband(this); } + public int calculateMaxPower() { + //we need to remove base power otherwise is problematic + int maxPower = 0; + for (final User user : getUsers()) { + maxPower += user.getMaxPower(); + } + return maxPower; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -258,8 +256,7 @@ public static void save() { Database.save(Faction.class, STORE.values().stream().toList()); } -// TODO(samu): import per-player power patch - public int calculateMaxPower(){ - return FactionsMod.CONFIG.POWER.BASE + (getUsers().size() * FactionsMod.CONFIG.POWER.MEMBER); + public int calculateRequiredPower(){ + return (getClaims().size()) * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT; } } \ No newline at end of file diff --git a/src/main/java/io/icker/factions/api/persistents/User.java b/src/main/java/io/icker/factions/api/persistents/User.java index 20c33f8c..2749c92c 100644 --- a/src/main/java/io/icker/factions/api/persistents/User.java +++ b/src/main/java/io/icker/factions/api/persistents/User.java @@ -12,6 +12,9 @@ import java.util.UUID; import java.util.stream.Collectors; +import static io.icker.factions.FactionsMod.CONFIG; +import static io.icker.factions.util.PermissionUtil.getPermissionPower; + @Name("User") public class User { private static final HashMap STORE = Database.load(User.class, User::getID); @@ -45,6 +48,10 @@ public enum SoundMode { @Field("Rank") public Rank rank; + @Field("Power") + private int power; + +// NOTE(CamperSamu): This should be private and have getters and setters with events in the API @Field("Radar") public boolean radar = false; @@ -62,11 +69,12 @@ public enum SoundMode { public User(UUID id) { this.id = id; + power = getMaxPower(); } public User() {} - @SuppressWarnings("unused") + @SuppressWarnings("unused") //util public String getKey() { return id.toString(); } @@ -90,6 +98,7 @@ public static void add(User user) { STORE.put(user.id, user); } +// NOTE(CamperSamu): Consider refactoring this to uuid(), following the record-like naming convention public UUID getID() { return id; } @@ -148,4 +157,40 @@ public static void save() { Database.save(User.class, STORE.values().stream().toList()); } + //region Power Management + public static int getMaxPower(final @NotNull UUID userUUID) { + final var user = get(userUUID); + return user != null ? user.getMaxPower() : 0; + } + + public int getMaxPower() { + return CONFIG.POWER.MEMBER + getPermissionPower(getID()); + } + + public static int getPower(final @NotNull UUID userUUID) { + return get(userUUID).getPower(); + } + + public int getPower() { + return power; + } + + @SuppressWarnings("unused") //util + public static int setPower(final @NotNull UUID userUUID, final int newPower) { + return get(userUUID).setPower(newPower); + } + + public int setPower(final int newPower) { + return power = newPower; + } + + @SuppressWarnings("unused") //util + public static int addPower(final @NotNull UUID userUUID, final int powerModifier) { + return get(userUUID).addPower(powerModifier); + } + + public int addPower(final int powerModifier) { + return power += powerModifier; + } + //endregion } \ No newline at end of file diff --git a/src/main/java/io/icker/factions/command/AdminCommand.java b/src/main/java/io/icker/factions/command/AdminCommand.java index 0b5033a1..04f9b70b 100644 --- a/src/main/java/io/icker/factions/command/AdminCommand.java +++ b/src/main/java/io/icker/factions/command/AdminCommand.java @@ -1,12 +1,10 @@ package io.icker.factions.command; import com.mojang.brigadier.arguments.IntegerArgumentType; -import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.LiteralCommandNode; import io.icker.factions.FactionsMod; -import io.icker.factions.api.persistents.Faction; import io.icker.factions.api.persistents.User; import io.icker.factions.util.Command; import io.icker.factions.util.Message; @@ -14,47 +12,53 @@ import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.util.Formatting; +import org.jetbrains.annotations.NotNull; + +import static io.icker.factions.api.persistents.User.get; +import static net.minecraft.text.Text.of; +import static net.minecraft.util.Formatting.GREEN; +import static net.minecraft.util.Formatting.RED; public class AdminCommand implements Command { - private int bypass(CommandContext context) throws CommandSyntaxException { + private int bypass(@NotNull CommandContext context) { ServerPlayerEntity player = context.getSource().getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute - User user = User.get(player.getUuid()); - boolean bypass = !user.bypass; - user.bypass = bypass; + User user = get(player.getUuid()); + user.bypass = !user.bypass; new Message("Successfully toggled claim bypass") .filler("ยท") .add( new Message(user.bypass ? "ON" : "OFF") - .format(user.bypass ? Formatting.GREEN : Formatting.RED) + .format(user.bypass ? GREEN : RED) ) .send(player, false); return 1; } - private int reload(CommandContext context) throws CommandSyntaxException { + private int reload(@NotNull CommandContext context) { FactionsMod.dynmap.reloadAll(); - new Message("Reloaded dynmap marker").send(context.getSource().getPlayer(), false); + context.getSource().sendFeedback(of("Reloaded dynmap marker"), true); //NOTE(CamperSamu): Avoid locking a reload command to be player-only, use the vanilla feedback function. return 1; } - private int power(CommandContext context) throws CommandSyntaxException { + private int power(@NotNull CommandContext context) throws CommandSyntaxException { ServerPlayerEntity player = context.getSource().getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute - Faction target = Faction.getByName(StringArgumentType.getString(context, "faction")); - int power = IntegerArgumentType.getInteger(context, "power"); + final User target = get(EntityArgumentType.getPlayer(context, "player").getUuid()); + final int power = IntegerArgumentType.getInteger(context, "power"); - int adjusted = target.adjustPower(power); + int adjusted = target.addPower(power); if (adjusted != 0) { if (power > 0) { new Message( "Admin %s added %d power", player.getName().getString(), adjusted - ).send(target); + ).send(player, false); new Message( "Added %d power", adjusted @@ -64,7 +68,7 @@ private int power(CommandContext context) throws CommandSyn "Admin %s removed %d power", player.getName().getString(), adjusted - ).send(target); + ).send(player, false); new Message( "Removed %d power", adjusted @@ -77,14 +81,15 @@ private int power(CommandContext context) throws CommandSyn return 1; } - private int spoof(CommandContext context) throws CommandSyntaxException { + private int spoof(@NotNull CommandContext context) throws CommandSyntaxException { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute - User user = User.get(player.getUuid()); + User user = get(player.getUuid()); ServerPlayerEntity targetEntity = EntityArgumentType.getPlayer(context, "player"); - User target = User.get(targetEntity.getUuid()); + User target = get(targetEntity.getUuid()); user.setSpoof(target); @@ -93,11 +98,12 @@ private int spoof(CommandContext context) throws CommandSyn return 1; } - private int clearSpoof(CommandContext context) throws CommandSyntaxException { + private int clearSpoof(@NotNull CommandContext context) { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute - User user = User.get(player.getUuid()); + User user = get(player.getUuid()); user.setSpoof(null); @@ -125,8 +131,7 @@ public LiteralCommandNode getNode() { .then( CommandManager.argument("power", IntegerArgumentType.integer()) .then( - CommandManager.argument("faction", StringArgumentType.greedyString()) - .suggests(Suggests.allFactions()) + CommandManager.argument("player", EntityArgumentType.player()) .executes(this::power) ) ) diff --git a/src/main/java/io/icker/factions/command/ClaimCommand.java b/src/main/java/io/icker/factions/command/ClaimCommand.java index bb2a49e2..51dde781 100644 --- a/src/main/java/io/icker/factions/command/ClaimCommand.java +++ b/src/main/java/io/icker/factions/command/ClaimCommand.java @@ -2,7 +2,6 @@ import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.LiteralCommandNode; import io.icker.factions.FactionsMod; import io.icker.factions.api.persistents.Claim; @@ -16,6 +15,7 @@ import net.minecraft.server.world.ServerWorld; import net.minecraft.util.Formatting; import net.minecraft.util.math.ChunkPos; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.HashMap; @@ -24,9 +24,10 @@ import java.util.stream.Collectors; public class ClaimCommand implements Command { - private int list(CommandContext context) throws CommandSyntaxException { + private int list(@NotNull CommandContext context) { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute List claims = Command.getUser(player).getFaction().getClaims(); int count = claims.size(); @@ -38,10 +39,10 @@ private int list(CommandContext context) throws CommandSynt if (count == 0) return 1; - HashMap> claimsMap = new HashMap>(); + HashMap> claimsMap = new HashMap<>(); claims.forEach(claim -> { - claimsMap.putIfAbsent(claim.level, new ArrayList()); + claimsMap.putIfAbsent(claim.level, new ArrayList<>()); claimsMap.get(claim.level).add(claim); }); @@ -64,15 +65,16 @@ private int list(CommandContext context) throws CommandSynt return 1; } - private int addForced(CommandContext context, int size) throws CommandSyntaxException { + private int addForced(@NotNull CommandContext context, int size) { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute ServerWorld world = player.getWorld(); Faction faction = Command.getUser(player).getFaction(); String dimension = world.getRegistryKey().getValue().toString(); - ArrayList chunks = new ArrayList(); + ArrayList chunks = new ArrayList<>(); for (int x = -size + 1; x < size; x++) { for (int y = -size + 1; y < size; y++) { @@ -116,12 +118,13 @@ private int addForced(CommandContext context, int size) thr return 1; } - private int add(CommandContext context) throws CommandSyntaxException { + private int add(@NotNull CommandContext context) { ServerPlayerEntity player = context.getSource().getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute Faction faction = Command.getUser(player).getFaction(); int requiredPower = (faction.getClaims().size() + 1) * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT; - int maxPower = faction.getUsers().size() * FactionsMod.CONFIG.POWER.MEMBER + FactionsMod.CONFIG.POWER.BASE; + int maxPower = faction.calculateMaxPower(); if (maxPower < requiredPower) { new Message("Not enough faction power to claim chunk").fail().send(player, false); @@ -131,15 +134,15 @@ private int add(CommandContext context) throws CommandSynta return addForced(context, 1); } - private int addSize(CommandContext context) throws CommandSyntaxException { - int size = IntegerArgumentType.getInteger(context, "size"); + private int addSize(CommandContext context) { + final int size = IntegerArgumentType.getInteger(context, "size"); ServerPlayerEntity player = context.getSource().getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute Faction faction = Command.getUser(player).getFaction(); - int requiredPower = (faction.getClaims().size() + 1) * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT; - int maxPower = faction.getUsers().size() * FactionsMod.CONFIG.POWER.MEMBER + FactionsMod.CONFIG.POWER.BASE; + final int requiredPower = (faction.getClaims().size() + 1) * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT; - if (maxPower < requiredPower) { + if (faction.calculateMaxPower() < requiredPower) { new Message("Not enough faction power to claim chunks").fail().send(player, false); return 0; } @@ -147,10 +150,11 @@ private int addSize(CommandContext context) throws CommandS return addForced(context, size); } - private int remove(CommandContext context) throws CommandSyntaxException { + private int remove(@NotNull CommandContext context) { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute ServerWorld world = player.getWorld(); ChunkPos chunkPos = world.getChunk(player.getBlockPos()).getPos(); @@ -185,11 +189,12 @@ private int remove(CommandContext context) throws CommandSy return 1; } - private int removeSize(CommandContext context) throws CommandSyntaxException { + private int removeSize(CommandContext context) { int size = IntegerArgumentType.getInteger(context, "size"); ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute ServerWorld world = player.getWorld(); String dimension = world.getRegistryKey().getValue().toString(); @@ -218,9 +223,10 @@ private int removeSize(CommandContext context) throws Comma return 1; } - private int removeAll(CommandContext context) throws CommandSyntaxException { + private int removeAll(@NotNull CommandContext context) { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute Faction faction = Command.getUser(player).getFaction(); @@ -232,9 +238,10 @@ private int removeAll(CommandContext context) throws Comman return 1; } - private int auto(CommandContext context) throws CommandSyntaxException { + private int auto(@NotNull CommandContext context) { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute User user = Command.getUser(player); user.autoclaim = !user.autoclaim; @@ -251,10 +258,11 @@ private int auto(CommandContext context) throws CommandSynt } @SuppressWarnings("") - private int setAccessLevel(CommandContext context, boolean increase) throws CommandSyntaxException { + private int setAccessLevel(@NotNull CommandContext context, boolean increase) { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute ServerWorld world = player.getWorld(); ChunkPos chunkPos = world.getChunk(player.getBlockPos()).getPos(); diff --git a/src/main/java/io/icker/factions/command/CreateCommand.java b/src/main/java/io/icker/factions/command/CreateCommand.java index bb44581a..2982542c 100644 --- a/src/main/java/io/icker/factions/command/CreateCommand.java +++ b/src/main/java/io/icker/factions/command/CreateCommand.java @@ -2,7 +2,6 @@ import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.LiteralCommandNode; import io.icker.factions.FactionsMod; import io.icker.factions.api.persistents.Faction; @@ -17,11 +16,12 @@ import java.util.Locale; public class CreateCommand implements Command { - private int run(CommandContext context) throws CommandSyntaxException { + private int run(CommandContext context) { String name = StringArgumentType.getString(context, "name"); ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute if (FactionsMod.CONFIG.DISPLAY.NAME_BLACKLIST.contains(name.toLowerCase(Locale.ROOT))) { new Message("Cannot create a faction with this name as it is on the blacklist").fail().send(player, false); @@ -38,7 +38,7 @@ private int run(CommandContext context) throws CommandSynta return 0; } - Faction faction = new Faction(name, "No description set", "No faction MOTD set", Formatting.WHITE, false, FactionsMod.CONFIG.POWER.BASE + FactionsMod.CONFIG.POWER.MEMBER); + Faction faction = new Faction(name, "No description set", "No faction MOTD set", Formatting.WHITE, false); Faction.add(faction); Command.getUser(player).joinFaction(faction.getID(), User.Rank.OWNER); diff --git a/src/main/java/io/icker/factions/command/InfoCommand.java b/src/main/java/io/icker/factions/command/InfoCommand.java index bf64b127..33280dfc 100644 --- a/src/main/java/io/icker/factions/command/InfoCommand.java +++ b/src/main/java/io/icker/factions/command/InfoCommand.java @@ -3,7 +3,6 @@ import com.mojang.authlib.GameProfile; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.LiteralCommandNode; import io.icker.factions.FactionsMod; import io.icker.factions.api.persistents.Faction; @@ -20,25 +19,42 @@ import java.util.List; import java.util.stream.Collectors; +import static net.minecraft.util.Formatting.*; + public class InfoCommand implements Command { - private int self(CommandContext context) throws CommandSyntaxException { + //region Constants + private static final String + UNCACHED_PLAYER_TEXT = "{Uncached Player}", + DELIMITER = ", "; + //endregion + + private int self(CommandContext context) { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute User user = Command.getUser(player); - if (!user.isInFaction()) { - new Message("Command can only be used whilst in a faction").fail().send(player, false); - return 0; + if (!user.isInFaction()) { //return player power + final String dashes = new StringBuilder("--------------------------------").substring(0, (32 - player.getName().getString().length())/2); +// final MutableText pinfo = Text.literal(dashes).append("[ ").formatted(BLACK).append(player.getName()).append(" ]").formatted(BLACK).append(dashes); + final var color = player.getName().getStyle().getColor() == null ? + WHITE : Formatting.byName(player.getName().getStyle().getColor().getName()); + new Message(BLACK + dashes + "[ " + color + player.getName().getString() + BLACK + " ]" + dashes).send(player, false); + new Message(GOLD + "Power: " + GREEN + user.getPower() + "/" + user.getMaxPower()) + .hover("Current / Max") + .send(player, false); + return 1; } return info(player, user.getFaction()); } - private int any(CommandContext context) throws CommandSyntaxException { + private int any(CommandContext context) { String factionName = StringArgumentType.getString(context, "faction"); ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute Faction faction = Faction.getByName(factionName); if (faction == null) { @@ -52,53 +68,54 @@ private int any(CommandContext context) throws CommandSynta public static int info(ServerPlayerEntity player, Faction faction) { List users = faction.getUsers(); + if (player.getServer() == null) return -1; UserCache cache = player.getServer().getUserCache(); - String owner = Formatting.WHITE + + + String owner = WHITE + users.stream() .filter(u -> u.rank == User.Rank.OWNER) - .map(user -> cache.getByUuid(user.getID()).orElse(new GameProfile(Util.NIL_UUID, "{Uncached Player}")).getName()) - .collect(Collectors.joining(", ")); + .map(user -> cache.getByUuid(user.getID()).orElse(new GameProfile(Util.NIL_UUID, UNCACHED_PLAYER_TEXT)).getName()) + .collect(Collectors.joining(DELIMITER)); String usersList = users.stream() - .map(user -> cache.getByUuid(user.getID()).orElse(new GameProfile(Util.NIL_UUID, "{Uncached Player}")).getName()) - .collect(Collectors.joining(", ")); + .map(user -> cache.getByUuid(user.getID()).orElse(new GameProfile(Util.NIL_UUID, UNCACHED_PLAYER_TEXT)).getName()) + .collect(Collectors.joining(DELIMITER)); String mutualAllies = faction.getMutualAllies().stream() .map(rel -> Faction.get(rel.target)) .map(fac -> fac.getColor() + fac.getName()) - .collect(Collectors.joining(Formatting.GRAY + ", ")); + .collect(Collectors.joining(Formatting.GRAY + DELIMITER)); String enemiesWith = Formatting.GRAY + faction.getEnemiesWith().stream() .map(rel -> Faction.get(rel.target)) .map(fac -> fac.getColor() + fac.getName()) - .collect(Collectors.joining(Formatting.GRAY + ", ")); + .collect(Collectors.joining(Formatting.GRAY + DELIMITER)); int requiredPower = faction.getClaims().size() * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT; - int maxPower = users.size() * FactionsMod.CONFIG.POWER.MEMBER + FactionsMod.CONFIG.POWER.BASE; // generate the --- int numDashes = 32 - faction.getName().length(); String dashes = new StringBuilder("--------------------------------").substring(0, numDashes/2); - new Message(Formatting.BLACK + dashes + "[ " + faction.getColor() + faction.getName() + Formatting.BLACK + " ]" + dashes) + new Message(BLACK + dashes + "[ " + faction.getColor() + faction.getName() + BLACK + " ]" + dashes) .send(player, false); - new Message(Formatting.GOLD + "Description: ") - .add(Formatting.WHITE + faction.getDescription()) + new Message(GOLD + "Description: ") + .add(WHITE + faction.getDescription()) .send(player, false); - new Message(Formatting.GOLD + "Owner: ") - .add(Formatting.WHITE + owner) + new Message(GOLD + "Owner: ") + .add(WHITE + owner) .send(player, false); - new Message(Formatting.GOLD + "Members (" + Formatting.WHITE.toString() + users.size() + Formatting.GOLD.toString() + "): ") + new Message(GOLD + "Members (" + WHITE + users.size() + GOLD + "): ") .add(usersList) .send(player, false); - new Message(Formatting.GOLD + "Power: ") - .add(Formatting.GREEN.toString() + faction.getPower() + slash() + requiredPower + slash() + maxPower) + new Message(GOLD + "Power: ") + .add(GREEN.toString() + faction.getPower() + slash() + requiredPower + slash() + faction.calculateMaxPower()) .hover("Current / Required / Max") .send(player, false); - new Message(Formatting.GREEN + "Allies (" + Formatting.WHITE + faction.getMutualAllies().size() + Formatting.GREEN + "): ") + new Message(GREEN + "Allies (" + WHITE + faction.getMutualAllies().size() + GREEN + "): ") .add(mutualAllies) .send(player, false); - new Message(Formatting.RED + "Enemies (" + Formatting.WHITE + faction.getEnemiesWith().size() + Formatting.RED + "): ") + new Message(RED + "Enemies (" + WHITE + faction.getEnemiesWith().size() + RED + "): ") .add(enemiesWith) .send(player, false); @@ -106,7 +123,7 @@ public static int info(ServerPlayerEntity player, Faction faction) { } private static String slash() { - return Formatting.GRAY + " / " + Formatting.GREEN; + return Formatting.GRAY + " / " + GREEN; } public LiteralCommandNode getNode() { diff --git a/src/main/java/io/icker/factions/command/JoinCommand.java b/src/main/java/io/icker/factions/command/JoinCommand.java index 2b93237a..bf0f1b14 100644 --- a/src/main/java/io/icker/factions/command/JoinCommand.java +++ b/src/main/java/io/icker/factions/command/JoinCommand.java @@ -2,22 +2,23 @@ import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.LiteralCommandNode; -import io.icker.factions.FactionsMod; import io.icker.factions.api.persistents.Faction; -import io.icker.factions.api.persistents.User; import io.icker.factions.util.Command; import io.icker.factions.util.Message; import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; +import static io.icker.factions.FactionsMod.CONFIG; +import static io.icker.factions.api.persistents.User.Rank.MEMBER; + public class JoinCommand implements Command { - private int run(CommandContext context) throws CommandSyntaxException { + private int run(CommandContext context) { String name = StringArgumentType.getString(context, "name"); ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute Faction faction = Faction.getByName(name); @@ -33,17 +34,16 @@ private int run(CommandContext context) throws CommandSynta return 0; } - if (FactionsMod.CONFIG.MAX_FACTION_SIZE != -1 && faction.getUsers().size() >= FactionsMod.CONFIG.MAX_FACTION_SIZE) { + if (CONFIG.MAX_FACTION_SIZE != -1 && faction.getUsers().size() >= CONFIG.MAX_FACTION_SIZE) { new Message("Cannot join faction as it is currently full").fail().send(player, false); return 0; } if (invited) faction.invites.remove(player.getUuid()); - Command.getUser(player).joinFaction(faction.getID(), User.Rank.MEMBER); + Command.getUser(player).joinFaction(faction.getID(), MEMBER); source.getServer().getPlayerManager().sendCommandTree(player); new Message(player.getName().getString() + " joined").send(faction); - faction.adjustPower(FactionsMod.CONFIG.POWER.MEMBER); return 1; } diff --git a/src/main/java/io/icker/factions/command/LeaveCommand.java b/src/main/java/io/icker/factions/command/LeaveCommand.java index 3863e36c..cd200c4a 100644 --- a/src/main/java/io/icker/factions/command/LeaveCommand.java +++ b/src/main/java/io/icker/factions/command/LeaveCommand.java @@ -1,9 +1,7 @@ package io.icker.factions.command; import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.LiteralCommandNode; -import io.icker.factions.FactionsMod; import io.icker.factions.api.persistents.Faction; import io.icker.factions.api.persistents.User; import io.icker.factions.util.Command; @@ -13,9 +11,10 @@ import net.minecraft.server.network.ServerPlayerEntity; public class LeaveCommand implements Command { - private int run(CommandContext context) throws CommandSyntaxException { + private int run(CommandContext context) { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute User user = Command.getUser(player); Faction faction = user.getFaction(); @@ -30,8 +29,6 @@ private int run(CommandContext context) throws CommandSynta if (faction.getUsers().size() == 0) { faction.remove(); - } else { - faction.adjustPower(-FactionsMod.CONFIG.POWER.MEMBER); } return 1; diff --git a/src/main/java/io/icker/factions/command/MemberCommand.java b/src/main/java/io/icker/factions/command/MemberCommand.java index 78c0b508..f4149b08 100644 --- a/src/main/java/io/icker/factions/command/MemberCommand.java +++ b/src/main/java/io/icker/factions/command/MemberCommand.java @@ -3,7 +3,6 @@ import com.mojang.authlib.GameProfile; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.LiteralCommandNode; import io.icker.factions.api.persistents.Faction; import io.icker.factions.api.persistents.User; @@ -20,9 +19,10 @@ import java.util.stream.Collectors; public class MemberCommand implements Command { - private int self(CommandContext context) throws CommandSyntaxException { + private int self(CommandContext context) { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute User user = Command.getUser(player); if (!user.isInFaction()) { @@ -33,11 +33,12 @@ private int self(CommandContext context) throws CommandSynt return members(player, user.getFaction()); } - private int any(CommandContext context) throws CommandSyntaxException { + private int any(CommandContext context) { String factionName = StringArgumentType.getString(context, "faction"); ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); + if (player == null) return -1; // Confirm that it's a player executing the command and not an entity with /execute Faction faction = Faction.getByName(factionName); if (faction == null) { @@ -50,6 +51,7 @@ private int any(CommandContext context) throws CommandSynta public static int members(ServerPlayerEntity player, Faction faction) { List users = faction.getUsers(); + if (player.getServer() == null) return -1; UserCache cache = player.getServer().getUserCache(); long memberCount = users.stream().filter(u -> u.rank == User.Rank.MEMBER).count(); @@ -83,7 +85,7 @@ public static int members(ServerPlayerEntity player, Faction faction) { int numDashes = 32 - faction.getName().length(); String dashes = new StringBuilder("--------------------------------").substring(0, numDashes/2); - new Message(Formatting.BLACK + dashes + "[ " + faction.getColor() + faction.getName() + Formatting.BLACK + " ]" + dashes) + new Message(Formatting.BLACK + dashes.toString() + "[ " + faction.getColor() + faction.getName() + Formatting.BLACK + " ]" + dashes) .send(player, false); new Message(Formatting.GOLD + "Total Members: ") .add(Formatting.WHITE.toString() + users.size()) @@ -91,13 +93,13 @@ public static int members(ServerPlayerEntity player, Faction faction) { new Message(Formatting.GOLD + "Owner: ") .add(owner) .send(player, false); - new Message(Formatting.GOLD + "Leaders (" + Formatting.WHITE.toString() + leaderCount + Formatting.GOLD.toString() + "): ") + new Message(Formatting.GOLD + "Leaders (" + Formatting.WHITE + leaderCount + Formatting.GOLD + "): ") .add(leaders) .send(player, false); - new Message(Formatting.GOLD + "Commanders (" + Formatting.WHITE.toString() + commanderCount + Formatting.GOLD.toString() + "): ") + new Message(Formatting.GOLD + "Commanders (" + Formatting.WHITE + commanderCount + Formatting.GOLD + "): ") .add(commanders) .send(player, false); - new Message(Formatting.GOLD + "Members (" + Formatting.WHITE.toString() + memberCount + Formatting.GOLD.toString() + "): ") + new Message(Formatting.GOLD + "Members (" + Formatting.WHITE + memberCount + Formatting.GOLD + "): ") .add(members) .send(player, false); diff --git a/src/main/java/io/icker/factions/command/SettingsCommand.java b/src/main/java/io/icker/factions/command/SettingsCommand.java index 88f4e0fe..bbf204c0 100644 --- a/src/main/java/io/icker/factions/command/SettingsCommand.java +++ b/src/main/java/io/icker/factions/command/SettingsCommand.java @@ -1,9 +1,7 @@ package io.icker.factions.command; import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.LiteralCommandNode; - import io.icker.factions.api.persistents.User; import io.icker.factions.util.Command; import io.icker.factions.util.Message; @@ -12,8 +10,10 @@ import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Formatting; +import static io.icker.factions.util.Command.Requires.hasPerms; + public class SettingsCommand implements Command{ - private int setChat(CommandContext context, User.ChatMode option) throws CommandSyntaxException { + private int setChat(CommandContext context, User.ChatMode option) { ServerPlayerEntity player = context.getSource().getPlayer(); User user = User.get(player.getUuid()); user.chat = option; @@ -45,7 +45,7 @@ private int setSounds(CommandContext context, User.SoundMod return 1; } - private int radar(CommandContext context) throws CommandSyntaxException { + private int radar(CommandContext context) { ServerCommandSource source = context.getSource(); ServerPlayerEntity player = source.getPlayer(); @@ -67,22 +67,22 @@ private int radar(CommandContext context) throws CommandSyn public LiteralCommandNode getNode() { return CommandManager .literal("settings") - .requires(Requires.hasPerms("factions.settings", 0)) + .requires(hasPerms("factions.settings", 0)) .then( CommandManager.literal("chat") - .requires(Requires.hasPerms("factions.settings.chat", 0)) + .requires(hasPerms("factions.settings.chat", 0)) .then(CommandManager.literal("global").executes(context -> setChat(context, User.ChatMode.GLOBAL))) .then(CommandManager.literal("faction").executes(context -> setChat(context, User.ChatMode.FACTION))) .then(CommandManager.literal("focus").executes(context -> setChat(context, User.ChatMode.FOCUS))) ) .then( CommandManager.literal("radar") - .requires(Requires.hasPerms("factions.settings.radar", 0)) + .requires(hasPerms("factions.settings.radar", 0)) .executes(this::radar) ) .then( CommandManager.literal("sounds") - .requires(Requires.hasPerms("factions.settings.sounds", 0)) + .requires(hasPerms("factions.settings.sounds", 0)) .then(CommandManager.literal("none").executes(context -> setSounds(context, User.SoundMode.NONE))) .then(CommandManager.literal("warnings").executes(context -> setSounds(context, User.SoundMode.WARNINGS))) .then(CommandManager.literal("faction").executes(context -> setSounds(context, User.SoundMode.FACTION))) diff --git a/src/main/java/io/icker/factions/config/PowerConfig.java b/src/main/java/io/icker/factions/config/PowerConfig.java index 95a3436b..53d705f4 100644 --- a/src/main/java/io/icker/factions/config/PowerConfig.java +++ b/src/main/java/io/icker/factions/config/PowerConfig.java @@ -1,28 +1,30 @@ package io.icker.factions.config; import com.google.gson.annotations.SerializedName; +import org.jetbrains.annotations.NotNull; public class PowerConfig { - @SerializedName("base") - public int BASE = 20; @SerializedName("member") public int MEMBER = 20; @SerializedName("claimWeight") - public int CLAIM_WEIGHT = 5; + public int CLAIM_WEIGHT = 2; @SerializedName("deathPenalty") - public int DEATH_PENALTY = 10; + public int DEATH_PENALTY = 5; @SerializedName("powerTicks") public PowerTicks POWER_TICKS = new PowerTicks(); + @SerializedName("pveDeathPenalty") + public boolean PVE_DEATH_PENALTY = false; + public static class PowerTicks { @SerializedName("ticks") public int TICKS = 12000; @SerializedName("reward") - public int REWARD = 1; + public int REWARD = 3; } } diff --git a/src/main/java/io/icker/factions/core/FactionsManager.java b/src/main/java/io/icker/factions/core/FactionsManager.java index 57005b04..16334d1c 100644 --- a/src/main/java/io/icker/factions/core/FactionsManager.java +++ b/src/main/java/io/icker/factions/core/FactionsManager.java @@ -1,12 +1,8 @@ package io.icker.factions.core; -import io.icker.factions.FactionsMod; -import io.icker.factions.api.events.FactionEvents; -import io.icker.factions.api.events.PlayerEvents; import io.icker.factions.api.persistents.Faction; import io.icker.factions.api.persistents.User; import io.icker.factions.util.Message; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket; @@ -15,20 +11,36 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.PlayerManager; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.Text; +import net.minecraft.text.MutableText; import net.minecraft.util.ActionResult; +import org.jetbrains.annotations.NotNull; + +import java.util.Objects; + +import static io.icker.factions.FactionsMod.CONFIG; +import static io.icker.factions.api.events.FactionEvents.*; +import static io.icker.factions.api.events.PlayerEvents.*; +import static java.lang.String.format; +import static net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents.SERVER_STARTED; +import static net.minecraft.text.Text.literal; +import static net.minecraft.text.Text.of; public class FactionsManager { + //region Constants + public static final String + POWER_LOST_MESSAGE = "%s lost %d power from dying", + POWER_GAINED_MESSAGE = "%s gained %d power from surviving"; public static PlayerManager playerManager; + //endregion public static void register() { - ServerLifecycleEvents.SERVER_STARTED.register(FactionsManager::serverStarted); - FactionEvents.MODIFY.register(FactionsManager::factionModified); - FactionEvents.MEMBER_JOIN.register(FactionsManager::memberChange); - FactionEvents.MEMBER_LEAVE.register(FactionsManager::memberChange); - PlayerEvents.ON_KILLED_BY_PLAYER.register(FactionsManager::playerDeath); - PlayerEvents.ON_POWER_TICK.register(FactionsManager::powerTick); - PlayerEvents.OPEN_SAFE.register(FactionsManager::openSafe); + SERVER_STARTED.register(FactionsManager::serverStarted); + MODIFY.register(FactionsManager::factionModified); + MEMBER_JOIN.register(FactionsManager::memberChange); + MEMBER_LEAVE.register(FactionsManager::memberChange); + ON_PLAYER_DEATH.register(FactionsManager::playerDeath); + ON_POWER_TICK.register(FactionsManager::powerTick); + OPEN_SAFE.register(FactionsManager::openSafe); } private static void serverStarted(MinecraftServer server) { @@ -38,10 +50,10 @@ private static void serverStarted(MinecraftServer server) { private static void factionModified(Faction faction) { ServerPlayerEntity[] players = faction.getUsers() - .stream() - .map(user -> playerManager.getPlayer(user.getID())) - .filter(player -> player != null) - .toArray(ServerPlayerEntity[]::new); + .stream() + .map(user -> playerManager.getPlayer(user.getID())) + .filter(Objects::nonNull) + .toArray(ServerPlayerEntity[]::new); updatePlayerList(players); } @@ -52,44 +64,49 @@ private static void memberChange(Faction faction, User user) { } } - private static void playerDeath(ServerPlayerEntity player, DamageSource source) { + private static void playerDeath(@NotNull ServerPlayerEntity player, DamageSource source, boolean killedByPlayer) { + if (!killedByPlayer && !CONFIG.POWER.PVE_DEATH_PENALTY) return; + User member = User.get(player.getUuid()); - if (!member.isInFaction()) return; + if (member.getPower() <= 0) return; + final int old = member.getPower(); + final int adjusted = member.addPower(member.getPower()-CONFIG.POWER.DEATH_PENALTY >= 0 ? -CONFIG.POWER.DEATH_PENALTY : -member.getPower()); - Faction faction = member.getFaction(); + final MutableText message = literal(format(POWER_LOST_MESSAGE, player.getName().getString(), old-adjusted)); - int adjusted = faction.adjustPower(-FactionsMod.CONFIG.POWER.DEATH_PENALTY); - new Message( - "%s lost %d power from dying", - player.getName().getString(), - adjusted - ).send(faction); + if (member.isInFaction()) { + final Faction faction = member.getFaction(); + new Message(message).send(faction); + } else { + player.sendMessage(message); + } } - private static void powerTick(ServerPlayerEntity player) { + private static void powerTick(@NotNull ServerPlayerEntity player) { User member = User.get(player.getUuid()); - if (!member.isInFaction()) return; + if (member.getPower() >= member.getMaxPower()) return; + final int old = member.getPower(); + final int adjusted = member.addPower(member.getPower() + CONFIG.POWER.POWER_TICKS.REWARD <= member.getMaxPower() ? CONFIG.POWER.POWER_TICKS.REWARD : member.getMaxPower() - member.getPower()); - Faction faction = member.getFaction(); + final MutableText message = literal(format(POWER_GAINED_MESSAGE, player.getName().getString(), adjusted-old)); - int adjusted = faction.adjustPower(FactionsMod.CONFIG.POWER.POWER_TICKS.REWARD); - if (adjusted != 0) - new Message( - "%s gained %d power from surviving", - player.getName().getString(), - adjusted - ).send(faction); + if (member.isInFaction()) { + final Faction faction = member.getFaction(); + new Message(message).send(faction); + } else { + player.sendMessage(message); + } } - private static void updatePlayerList(ServerPlayerEntity ...players) { + private static void updatePlayerList(ServerPlayerEntity... players) { playerManager.sendToAll(new PlayerListS2CPacket(PlayerListS2CPacket.Action.UPDATE_DISPLAY_NAME, players)); } private static ActionResult openSafe(PlayerEntity player, Faction faction) { - User user = User.get(player.getUuid()); + User user = User.get(player.getUuid()); if (!user.isInFaction()) { - if (FactionsMod.CONFIG.SAFE != null && FactionsMod.CONFIG.SAFE.ENDER_CHEST) { + if (CONFIG.SAFE != null && CONFIG.SAFE.ENDER_CHEST) { new Message("Cannot use enderchests when not in a faction").fail().send(player, false); return ActionResult.FAIL; } @@ -97,16 +114,16 @@ private static ActionResult openSafe(PlayerEntity player, Faction faction) { } player.openHandledScreen( - new SimpleNamedScreenHandlerFactory( - (syncId, inventory, p) -> { - if (FactionsMod.CONFIG.SAFE.DOUBLE) { - return GenericContainerScreenHandler.createGeneric9x6(syncId, inventory, faction.getSafe()); - } else { - return GenericContainerScreenHandler.createGeneric9x3(syncId, inventory, faction.getSafe()); - } - }, - Text.of(String.format("%s's Safe", faction.getName())) - ) + new SimpleNamedScreenHandlerFactory( + (syncId, inventory, p) -> { + if (CONFIG.SAFE != null && CONFIG.SAFE.DOUBLE) { + return GenericContainerScreenHandler.createGeneric9x6(syncId, inventory, faction.getSafe()); + } else { + return GenericContainerScreenHandler.createGeneric9x3(syncId, inventory, faction.getSafe()); + } + }, + of(format("%s's Safe", faction.getName())) + ) ); return ActionResult.SUCCESS; diff --git a/src/main/java/io/icker/factions/core/WorldManager.java b/src/main/java/io/icker/factions/core/WorldManager.java index 0f829afe..332d5790 100644 --- a/src/main/java/io/icker/factions/core/WorldManager.java +++ b/src/main/java/io/icker/factions/core/WorldManager.java @@ -34,9 +34,8 @@ private static void onMove(ServerPlayerEntity player) { if (user.autoclaim && claim == null) { Faction faction = user.getFaction(); int requiredPower = (faction.getClaims().size() + 1) * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT; - int maxPower = faction.getUsers().size() * FactionsMod.CONFIG.POWER.MEMBER + FactionsMod.CONFIG.POWER.BASE; - if (maxPower < requiredPower) { + if (faction.calculateMaxPower() < requiredPower) { new Message("Not enough faction power to claim chunk, autoclaim toggled off").fail().send(player, false); user.autoclaim = false; } else { diff --git a/src/main/java/io/icker/factions/mixin/ServerPlayerEntityMixin.java b/src/main/java/io/icker/factions/mixin/ServerPlayerEntityMixin.java index 6c924c8d..85dbfe56 100644 --- a/src/main/java/io/icker/factions/mixin/ServerPlayerEntityMixin.java +++ b/src/main/java/io/icker/factions/mixin/ServerPlayerEntityMixin.java @@ -30,15 +30,14 @@ protected ServerPlayerEntityMixin(EntityType entityType, @Inject(at = @At("HEAD"), method = "setClientSettings") public void setClientSettings(ClientSettingsC2SPacket packet, CallbackInfo info) { - User member = User.get(((ServerPlayerEntity)(Object) this).getUuid()); + User member = User.get(getUuid()); member.language = packet.language(); } @Inject(at = @At("HEAD"), method = "onDeath") public void onDeath(DamageSource source, CallbackInfo info) { Entity entity = source.getSource(); - if (entity == null || !entity.isPlayer()) return; - PlayerEvents.ON_KILLED_BY_PLAYER.invoker().onKilledByPlayer((ServerPlayerEntity) (Object) this, source); + PlayerEvents.ON_PLAYER_DEATH.invoker().onPlayerDeath((ServerPlayerEntity) (Object) this, source, entity == null || !entity.isPlayer()); } @Inject(at = @At("HEAD"), method = "tick") @@ -58,15 +57,15 @@ private void isInvulnerableTo(DamageSource damageSource, CallbackInfoReturnable< @Inject(method = "getPlayerListName", at = @At("HEAD"), cancellable = true) public void getPlayerListName(CallbackInfoReturnable cir) { if (FactionsMod.CONFIG.DISPLAY.TAB_MENU) { - User member = User.get(((ServerPlayerEntity) (Object) this).getUuid()); + User member = User.get(getUuid()); if (member.isInFaction()) { Faction faction = member.getFaction(); cir.setReturnValue(new Message(String.format("[%s] ", faction.getName())).format(faction.getColor()).add( - new Message(((ServerPlayerEntity) (Object) this).getName().getString()).format(Formatting.WHITE) + new Message(getName().getString()).format(Formatting.WHITE) ).raw()); } else { cir.setReturnValue(new Message("[FACTIONLESS] ").format(Formatting.GRAY).add( - new Message(((ServerPlayerEntity) (Object) this).getName().getString()).format(Formatting.WHITE) + new Message(getName().getString()).format(Formatting.WHITE) ).raw()); } } diff --git a/src/main/java/io/icker/factions/util/Command.java b/src/main/java/io/icker/factions/util/Command.java index 53b2bb88..1f118af1 100644 --- a/src/main/java/io/icker/factions/util/Command.java +++ b/src/main/java/io/icker/factions/util/Command.java @@ -1,10 +1,7 @@ package io.icker.factions.util; -import java.util.function.Predicate; - import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.tree.LiteralCommandNode; - import io.icker.factions.FactionsMod; import io.icker.factions.api.persistents.Faction; import io.icker.factions.api.persistents.User; @@ -12,17 +9,28 @@ import net.fabricmc.loader.api.FabricLoader; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import java.util.function.Predicate; public interface Command { - public LiteralCommandNode getNode(); - public static final boolean permissions = FabricLoader.getInstance().isModLoaded("fabric-permissions-api-v0"); + boolean permissions = FabricLoader.getInstance().isModLoaded("fabric-permissions-api-v0"); - public interface Requires { - boolean run(User user); + static User getUser(@NotNull ServerPlayerEntity player) { + User user = User.get(player.getUuid()); + if (user.getSpoof() == null) { + return user; + } + return user.getSpoof(); + } + + LiteralCommandNode getNode(); + interface Requires { + @Contract(pure = true) @SafeVarargs - public static Predicate multiple(Predicate... args) { + static @NotNull Predicate multiple(Predicate... args) { return source -> { for (Predicate predicate : args) { if (!predicate.test(source)) return false; @@ -32,97 +40,110 @@ public static Predicate multiple(Predicate isFactionless() { + @Contract(pure = true) + static @NotNull Predicate isFactionless() { return require(user -> !user.isInFaction()); } - public static Predicate isMember() { - return require(user -> user.isInFaction()); + @Contract(pure = true) + static @NotNull Predicate isMember() { + return require(User::isInFaction); } - public static Predicate isCommander() { + @Contract(pure = true) + static @NotNull Predicate isCommander() { return require(user -> user.rank == User.Rank.COMMANDER || user.rank == User.Rank.LEADER || user.rank == User.Rank.OWNER); } - public static Predicate isLeader() { + @Contract(pure = true) + static @NotNull Predicate isLeader() { return require(user -> user.rank == User.Rank.LEADER || user.rank == User.Rank.OWNER); } - public static Predicate isOwner() { + @Contract(pure = true) + static @NotNull Predicate isOwner() { return require(user -> user.rank == User.Rank.OWNER); } - - public static Predicate isAdmin() { + + @Contract(pure = true) + @SuppressWarnings("unused") //util + static @NotNull Predicate isAdmin() { return source -> source.hasPermissionLevel(FactionsMod.CONFIG.REQUIRED_BYPASS_LEVEL); } - public static Predicate hasPerms(String permission, int defaultValue) { + @Contract(pure = true) + static @NotNull Predicate hasPerms(String permission, int defaultValue) { return source -> !permissions || Permissions.check(source, permission, defaultValue); } - public static Predicate require(Requires req) { + @Contract(pure = true) + static @NotNull Predicate require(Requires req) { return source -> { - ServerPlayerEntity entity = source.getPlayer(); - User user = Command.getUser(entity); + ServerPlayerEntity player = source.getPlayer(); + if (player == null) + return false; // Confirm that it's a player executing the command and not an entity with /execute (or console) + User user = Command.getUser(player); return req.run(user); }; } - } - public interface Suggests { - String[] run(User user); + boolean run(User user); + } - public static SuggestionProvider allFactions() { + interface Suggests { + @Contract(pure = true) + static @NotNull SuggestionProvider allFactions() { return allFactions(true); } - public static SuggestionProvider allFactions(boolean includeYou) { - return suggest(user -> - Faction.all() - .stream() - .filter(f -> includeYou || !user.isInFaction() || !user.getFaction().getID().equals(f.getID())) - .map(f -> f.getName()) - .toArray(String[]::new) + @Contract(pure = true) + static @NotNull SuggestionProvider allFactions(boolean includeYou) { + return suggest(user -> + Faction.all() + .stream() + .filter(f -> includeYou || !user.isInFaction() || !user.getFaction().getID().equals(f.getID())) + .map(Faction::getName) + .toArray(String[]::new) ); } - public static SuggestionProvider openFactions() { + @Contract(pure = true) + @SuppressWarnings("unused") //util + static @NotNull SuggestionProvider openFactions() { return suggest(user -> - Faction.all() - .stream() - .filter(f -> f.isOpen()) - .map(f -> f.getName()) - .toArray(String[]::new) + Faction.all() + .stream() + .filter(Faction::isOpen) + .map(Faction::getName) + .toArray(String[]::new) ); } - public static SuggestionProvider openInvitedFactions() { + @Contract(pure = true) + static @NotNull SuggestionProvider openInvitedFactions() { return suggest(user -> - Faction.all() - .stream() - .filter(f -> f.isOpen() || f.isInvited(user.getID())) - .map(f -> f.getName()) - .toArray(String[]::new) + Faction.all() + .stream() + .filter(f -> f.isOpen() || f.isInvited(user.getID())) + .map(Faction::getName) + .toArray(String[]::new) ); } - public static SuggestionProvider suggest(Suggests sug) { + @Contract(pure = true) + static @NotNull SuggestionProvider suggest(Suggests sug) { return (context, builder) -> { - ServerPlayerEntity entity = context.getSource().getPlayer(); - User user = User.get(entity.getUuid()); + ServerPlayerEntity player = context.getSource().getPlayer(); + if (player == null) + return null; // Confirm that it's a player executing the command and not an entity with /execute + User user = User.get(player.getUuid()); for (String suggestion : sug.run(user)) { builder.suggest(suggestion); } return builder.buildFuture(); }; } - } - public static User getUser(ServerPlayerEntity player) { - User user = User.get(player.getUuid()); - if (user.getSpoof() == null) { - return user; - } - return user.getSpoof(); + String[] run(User user); } } \ No newline at end of file diff --git a/src/main/java/io/icker/factions/util/Message.java b/src/main/java/io/icker/factions/util/Message.java index cffc35cd..81d530b7 100644 --- a/src/main/java/io/icker/factions/util/Message.java +++ b/src/main/java/io/icker/factions/util/Message.java @@ -10,6 +10,7 @@ import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import org.jetbrains.annotations.NotNull; public class Message { public static PlayerManager manager; @@ -19,6 +20,10 @@ public Message(String message) { text = (MutableText) Text.of(message); } + public Message(MutableText message) { + text = message; + } + public Message(String message, Object... args) { text = (MutableText) Text.of(String.format(message, args)); } @@ -72,6 +77,7 @@ public Message send(Faction faction) { return this; } + @SuppressWarnings("unused") //util public void sendToGlobalChat() { for (ServerPlayerEntity player : manager.getPlayerList()) { User.ChatMode option = User.get(player.getUuid()).chat; @@ -79,9 +85,11 @@ public void sendToGlobalChat() { } } - public void sendToFactionChat(Faction faction) { + @SuppressWarnings("unused") //util + public void sendToFactionChat(@NotNull Faction faction) { for (User member : faction.getUsers()) { ServerPlayerEntity player = manager.getPlayer(member.getID()); + if (player == null) return; // Make sure that player is online player.sendMessage(text, false); } } diff --git a/src/main/java/io/icker/factions/util/PermissionUtil.java b/src/main/java/io/icker/factions/util/PermissionUtil.java new file mode 100644 index 00000000..edd4bf6b --- /dev/null +++ b/src/main/java/io/icker/factions/util/PermissionUtil.java @@ -0,0 +1,37 @@ +package io.icker.factions.util; + +import net.luckperms.api.LuckPermsProvider; +import net.luckperms.api.model.group.Group; +import net.luckperms.api.node.Node; +import net.luckperms.api.query.QueryOptions; + +import java.util.UUID; + +public abstract class PermissionUtil { + public static int getPermissionPower(UUID uuid){ + int power = 0; + try { + + var user = LuckPermsProvider.get().getUserManager().getUser(uuid); + if (user == null) return 0; + for (Group inheritedGroup : user.getInheritedGroups(QueryOptions.nonContextual())) { + for (Node node : inheritedGroup.getNodes()) { + var perm = node.getKey(); + if (perm.contains("factions.power.modifier.")) { + int value = getNumberFromPermission(node.getKey()); + power+=value; + } + } + } + } catch (NoClassDefFoundError | Exception ignored) {} + return power; + } + + public static int getNumberFromPermission(String permission) { + try { + return Integer.parseInt(permission.substring(permission.lastIndexOf(".")+1)); + } catch (Exception ignored) { + return 0; + } + } +} diff --git a/src/main/java/io/icker/factions/util/PlaceholdersWrapper.java b/src/main/java/io/icker/factions/util/PlaceholdersWrapper.java index 6a70d116..ed687d1d 100644 --- a/src/main/java/io/icker/factions/util/PlaceholdersWrapper.java +++ b/src/main/java/io/icker/factions/util/PlaceholdersWrapper.java @@ -1,6 +1,5 @@ package io.icker.factions.util; -import io.icker.factions.FactionsMod; import io.icker.factions.api.persistents.User; import net.minecraft.text.Style; import net.minecraft.text.Text; @@ -9,10 +8,14 @@ import static eu.pb4.placeholders.api.PlaceholderResult.invalid; import static eu.pb4.placeholders.api.PlaceholderResult.value; +import static eu.pb4.placeholders.api.Placeholders.parsePlaceholder; import static eu.pb4.placeholders.api.Placeholders.register; import static io.icker.factions.FactionsMod.MODID; import static java.lang.Integer.*; +import static net.minecraft.text.Text.empty; +import static net.minecraft.text.Text.literal; import static net.minecraft.util.Formatting.DARK_GRAY; +import static net.minecraft.util.Formatting.GRAY; public class PlaceholdersWrapper { public static final Identifier FACTION_NAME_ID = new Identifier(MODID, "name"); @@ -25,10 +28,14 @@ public class PlaceholdersWrapper { public static final Identifier FACTION_POWER_FORMATTED_ID = new Identifier(MODID, "power_formatted"); public static final Identifier FACTION_MAX_POWER_ID = new Identifier(MODID, "max_power"); public static final Identifier FACTION_PLAYER_POWER_ID = new Identifier(MODID, "player_power"); + public static final Identifier FACTION_PLAYER_POWER_FORMATTED_ID = new Identifier(MODID, "player_power_formatted"); + public static final Identifier FACTION_PLAYER_MAX_POWER_ID = new Identifier(MODID, "player_max_power"); public static final Identifier FACTION_REQUIRED_POWER_ID = new Identifier(MODID, "required_power"); public static final Identifier FACTION_REQUIRED_POWER_FORMATTED_ID = new Identifier(MODID, "required_power_formatted"); + public static final Identifier FACTION_POWER_STATS_ID = new Identifier(MODID, "power_stats"); + public static final Identifier FACTION_POWER_STATS_FORMATTED_ID = new Identifier(MODID, "power_stats_formatted"); public static final String NULL_STRING = "N/A"; - public static final Text NULL_TEXT = Text.literal(NULL_STRING).formatted(DARK_GRAY); + public static final Text NULL_TEXT = literal(NULL_STRING).formatted(DARK_GRAY); public static void init() { register(FACTION_NAME_ID, (ctx, argument) -> { @@ -43,7 +50,7 @@ public static void init() { final var faction = member.getFaction(); if (faction != null) - r = Text.literal(faction.getName()).formatted(member.getFaction().getColor()); + r = literal(faction.getName()).formatted(member.getFaction().getColor()); return value(r); }); @@ -149,7 +156,7 @@ public static void init() { if (faction != null) { final int red = mapBoundRange(faction.calculateMaxPower(), 0, 170, 255, faction.getPower()); final int green = mapBoundRange(0, faction.calculateMaxPower(), 170, 255, faction.getPower()); - r = Text.literal("" + faction.getPower()).setStyle(Style.EMPTY.withColor(TextColor.parse("#" + toHexString(red) + toHexString(green) + "AA"))); + r = literal("" + faction.getPower()).setStyle(Style.EMPTY.withColor(TextColor.parse("#" + toHexString(red) + toHexString(green) + "AA"))); } return value(r); @@ -171,7 +178,36 @@ public static void init() { }); register(FACTION_PLAYER_POWER_ID, (ctx, argument) -> { - String r = "" + FactionsMod.CONFIG.POWER.MEMBER; + if (!ctx.hasPlayer()) return invalid("No Player!"); + assert ctx.player() != null; + + final var member = User.get(ctx.player().getUuid()); + + String r = "" + member.getPower(); + + return value(r); + }); + + register(FACTION_PLAYER_POWER_FORMATTED_ID, (ctx, argument) -> { + if (!ctx.hasPlayer()) return invalid("No Player!"); + assert ctx.player() != null; + + final var member = User.get(ctx.player().getUuid()); + + final int red = mapBoundRange(member.getMaxPower(), 0, 170, 255, member.getPower()); + final int green = mapBoundRange(0, member.getMaxPower(), 170, 255, member.getPower()); + + return value(literal("" + member.getPower()).setStyle(Style.EMPTY.withColor(TextColor.parse("#" + toHexString(red) + toHexString(green) + "AA")))); + }); + + register(FACTION_PLAYER_MAX_POWER_ID, (ctx, argument) -> { + if (!ctx.hasPlayer()) return invalid("No Player!"); + assert ctx.player() != null; + + final var member = User.get(ctx.player().getUuid()); + + String r = "" + member.getMaxPower(); + return value(r); }); @@ -186,7 +222,7 @@ public static void init() { final var faction = member.getFaction(); if (faction != null) - r = "" + faction.getClaims().size() * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT; + r = "" + faction.calculateRequiredPower(); return value(r); }); @@ -203,17 +239,69 @@ public static void init() { if (faction != null) { - final int reqPower = faction.getClaims().size() * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT; + final int reqPower = faction.calculateRequiredPower(); final int red = mapBoundRange(0, faction.getPower(), 85, 255, reqPower); - r = Text.literal("" + reqPower).setStyle(Style.EMPTY.withColor(TextColor.parse("#" + toHexString(red) + "5555"))); + r = literal("" + reqPower).setStyle(Style.EMPTY.withColor(TextColor.parse("#" + toHexString(red) + "5555"))); } return value(r); }); + + register(FACTION_POWER_STATS_ID, (ctx, argument) -> { + if (!ctx.hasPlayer()) return invalid("No Player!"); + assert ctx.player() != null; + + final var member = User.get(ctx.player().getUuid()); + if (member.isInFaction()) { + return value( + empty() + .append(parsePlaceholder(FACTION_POWER_ID, argument, ctx).text()) + .append(literal("/").formatted(DARK_GRAY)) + .append(parsePlaceholder(FACTION_REQUIRED_POWER_ID, argument, ctx).text()) + .append(literal("/").formatted(DARK_GRAY)) + .append(parsePlaceholder(FACTION_MAX_POWER_ID, argument, ctx).text()) + ); + } else { + return value( + empty() + .append(parsePlaceholder(FACTION_PLAYER_POWER_ID, argument, ctx).text()) + .append(literal("/").formatted(DARK_GRAY)) + .append(parsePlaceholder(FACTION_PLAYER_MAX_POWER_ID, argument, ctx).text()) + ); + } + }); + + register(FACTION_POWER_STATS_FORMATTED_ID, (ctx, argument) -> { + if (!ctx.hasPlayer()) return invalid("No Player!"); + assert ctx.player() != null; + + final var member = User.get(ctx.player().getUuid()); + if (member.isInFaction()) { + return value( + empty() + .append(parsePlaceholder(FACTION_POWER_FORMATTED_ID, argument, ctx).text()) + .append(literal("/").formatted(DARK_GRAY)) + .append(parsePlaceholder(FACTION_REQUIRED_POWER_FORMATTED_ID, argument, ctx).text()) + .append(literal("/").formatted(DARK_GRAY)) + .append(parsePlaceholder(FACTION_MAX_POWER_ID, argument, ctx).text().copy().formatted(GRAY)) + ); + } else { + return value( + empty() + .append(parsePlaceholder(FACTION_PLAYER_POWER_FORMATTED_ID, argument, ctx).text()) + .append(literal("/").formatted(DARK_GRAY)) + .append(parsePlaceholder(FACTION_PLAYER_MAX_POWER_ID, argument, ctx).text().copy().formatted(GRAY)) + ); + } + }); } - @SuppressWarnings("all") + @SuppressWarnings("all") //math utils private static int mapBoundRange(int a1, int a2, int b1, int b2, int s) { - return min(b2, max(b1, b1 + ((s - a1) * (b2 - b1)) / (a2 - a1))); + try { + return min(b2, max(b1, b1 + ((s - a1) * (b2 - b1)) / (a2 - a1))); + } catch (ArithmeticException ignored){ + return 0; + } } }