NEW_DECLARATION =
12 | EventFactory.createArrayBacked(
13 | NewDecleration.class,
14 | callbacks ->
15 | (relationship) -> {
16 | for (NewDecleration callback : callbacks) {
17 | callback.onNewDecleration(relationship);
18 | }
19 | });
20 |
21 | /**
22 | * When two factions are declared to have the same status
23 | *
24 | * For example, mutual allies
25 | */
26 | public static final Event NEW_MUTUAL =
27 | EventFactory.createArrayBacked(
28 | NewMutual.class,
29 | callbacks ->
30 | (relationship) -> {
31 | for (NewMutual callback : callbacks) {
32 | callback.onNewMutual(relationship);
33 | }
34 | });
35 |
36 | /** When a mutual relationship is ended by either of the two factions */
37 | public static final Event END_MUTUAL =
38 | EventFactory.createArrayBacked(
39 | EndMutual.class,
40 | callbacks ->
41 | (relationship, oldStatus) -> {
42 | for (EndMutual callback : callbacks) {
43 | callback.onEndMutual(relationship, oldStatus);
44 | }
45 | });
46 |
47 | @FunctionalInterface
48 | public interface NewDecleration {
49 | void onNewDecleration(Relationship relationship);
50 | }
51 |
52 | @FunctionalInterface
53 | public interface NewMutual {
54 | void onNewMutual(Relationship relationship);
55 | }
56 |
57 | @FunctionalInterface
58 | public interface EndMutual {
59 | void onEndMutual(Relationship relationship, Relationship.Status oldStatus);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/mixin/PlayerManagerMixin.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.mixin;
2 |
3 | import io.icker.factions.api.persistents.User;
4 | import io.icker.factions.util.StyledChatCompatibility;
5 |
6 | import net.fabricmc.loader.api.FabricLoader;
7 | import net.minecraft.network.message.MessageType;
8 | import net.minecraft.network.message.SentMessage;
9 | import net.minecraft.server.PlayerManager;
10 | import net.minecraft.server.network.ServerPlayerEntity;
11 |
12 | import org.spongepowered.asm.mixin.Mixin;
13 | import org.spongepowered.asm.mixin.injection.At;
14 | import org.spongepowered.asm.mixin.injection.Redirect;
15 |
16 | @Mixin(PlayerManager.class)
17 | public class PlayerManagerMixin {
18 | @Redirect(
19 | method =
20 | "broadcast(Lnet/minecraft/network/message/SignedMessage;Ljava/util/function/Predicate;Lnet/minecraft/server/network/ServerPlayerEntity;Lnet/minecraft/network/message/MessageType$Parameters;)V",
21 | at =
22 | @At(
23 | value = "INVOKE",
24 | target =
25 | "Lnet/minecraft/server/network/ServerPlayerEntity;sendChatMessage(Lnet/minecraft/network/message/SentMessage;ZLnet/minecraft/network/message/MessageType$Parameters;)V"))
26 | public void sendChatMessage(
27 | ServerPlayerEntity player,
28 | SentMessage message,
29 | boolean bl,
30 | MessageType.Parameters parameters) {
31 | if (message instanceof SentMessage.Profileless
32 | || (FabricLoader.getInstance().isModLoaded("styledchat")
33 | && StyledChatCompatibility.isNotPlayer(message))) {
34 | player.sendChatMessage(message, bl, parameters);
35 | return;
36 | }
37 |
38 | User sender;
39 |
40 | if (FabricLoader.getInstance().isModLoaded("styledchat")) {
41 | sender = User.get(StyledChatCompatibility.getSender(message));
42 | } else {
43 | sender = User.get(((SentMessage.Chat) message).message().link().sender());
44 | }
45 |
46 | User target = User.get(player.getUuid());
47 |
48 | if (sender.chat == User.ChatMode.GLOBAL && target.chat != User.ChatMode.FOCUS) {
49 | player.sendChatMessage(message, bl, parameters);
50 | }
51 |
52 | if ((sender.chat == User.ChatMode.FACTION || sender.chat == User.ChatMode.FOCUS)
53 | && sender.getFaction().equals(target.getFaction())) {
54 | player.sendChatMessage(message, bl, parameters);
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/mixin/ExplosionBehaviorMixin.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.mixin;
2 |
3 | import io.icker.factions.api.events.PlayerEvents;
4 |
5 | import net.minecraft.block.BlockState;
6 | import net.minecraft.entity.Entity;
7 | import net.minecraft.util.ActionResult;
8 | import net.minecraft.util.math.BlockPos;
9 | import net.minecraft.world.BlockView;
10 | import net.minecraft.world.explosion.Explosion;
11 | import net.minecraft.world.explosion.ExplosionBehavior;
12 | import net.minecraft.world.explosion.ExplosionImpl;
13 |
14 | import org.spongepowered.asm.mixin.Mixin;
15 | import org.spongepowered.asm.mixin.injection.At;
16 | import org.spongepowered.asm.mixin.injection.Redirect;
17 |
18 | @Mixin(ExplosionImpl.class)
19 | public class ExplosionBehaviorMixin {
20 | @Redirect(
21 | method = "getBlocksToDestroy",
22 | at =
23 | @At(
24 | value = "INVOKE",
25 | target =
26 | "Lnet/minecraft/world/explosion/ExplosionBehavior;canDestroyBlock(Lnet/minecraft/world/explosion/Explosion;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;F)Z"))
27 | public boolean canDestroyBlock(
28 | ExplosionBehavior behavior,
29 | Explosion explosion,
30 | BlockView world,
31 | BlockPos pos,
32 | BlockState state,
33 | float power) {
34 | ActionResult result =
35 | PlayerEvents.EXPLODE_BLOCK.invoker().onExplodeBlock(explosion, world, pos, state);
36 | if (result.isAccepted()) {
37 | return true;
38 | } else if (result == ActionResult.FAIL) {
39 | return false;
40 | }
41 |
42 | return behavior.canDestroyBlock(explosion, world, pos, state, power);
43 | }
44 |
45 | @Redirect(
46 | method = "damageEntities",
47 | at =
48 | @At(
49 | value = "INVOKE",
50 | target =
51 | "Lnet/minecraft/entity/Entity;isImmuneToExplosion(Lnet/minecraft/world/explosion/Explosion;)Z"))
52 | public boolean shouldDamage(Entity entity, Explosion explosion) {
53 | ActionResult result =
54 | PlayerEvents.EXPLODE_DAMAGE.invoker().onExplodeDamage(explosion, entity);
55 | if (result.isAccepted()) {
56 | return false;
57 | } else if (result == ActionResult.FAIL) {
58 | return true;
59 | }
60 |
61 | return entity.isImmuneToExplosion(explosion);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/ui/InputGui.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.ui;
2 |
3 | import eu.pb4.sgui.api.elements.GuiElementBuilder;
4 | import eu.pb4.sgui.api.gui.AnvilInputGui;
5 |
6 | import io.icker.factions.util.GuiInteract;
7 |
8 | import net.minecraft.component.DataComponentTypes;
9 | import net.minecraft.item.ItemStack;
10 | import net.minecraft.item.Items;
11 | import net.minecraft.server.network.ServerPlayerEntity;
12 | import net.minecraft.sound.SoundEvent;
13 | import net.minecraft.text.MutableText;
14 | import net.minecraft.text.Style;
15 | import net.minecraft.text.Text;
16 | import net.minecraft.util.Formatting;
17 | import net.minecraft.util.Identifier;
18 |
19 | import java.util.Objects;
20 | import java.util.Timer;
21 | import java.util.TimerTask;
22 |
23 | public class InputGui extends AnvilInputGui {
24 | public GuiElementBuilder returnBtn;
25 | public GuiElementBuilder confirmBtn;
26 | private final Timer timer = new Timer();
27 |
28 | public InputGui(ServerPlayerEntity player) {
29 | super(player, false);
30 |
31 | this.returnBtn =
32 | new GuiElementBuilder(Items.BARRIER)
33 | .setName(
34 | Text.translatable("factions.gui.generic.back")
35 | .formatted(Formatting.RED));
36 | this.confirmBtn =
37 | new GuiElementBuilder(Items.SLIME_BALL)
38 | .setName(
39 | Text.translatable("factions.gui.generic.confirm")
40 | .formatted(Formatting.GREEN));
41 | }
42 |
43 | public void showErrorMessage(String text, int slotIndex) {
44 | showErrorMessage(Text.literal(text), slotIndex);
45 | }
46 |
47 | public void showErrorMessage(MutableText text, int slotIndex) {
48 | ItemStack item = Objects.requireNonNull(this.getSlot(slotIndex)).getItemStack();
49 | item.set(
50 | DataComponentTypes.CUSTOM_NAME,
51 | text.setStyle(Style.EMPTY.withItalic(false).withColor(Formatting.RED)));
52 | GuiInteract.playSound(
53 | player, SoundEvent.of(Identifier.of("minecraft:item.shield.break")), 1f, 1f);
54 | timer.schedule(
55 | new TimerTask() {
56 | @Override
57 | public void run() {
58 | item.remove(DataComponentTypes.CUSTOM_NAME);
59 | }
60 | },
61 | 1500);
62 | }
63 |
64 | @Override
65 | public boolean open() {
66 | this.setSlot(1, returnBtn);
67 | this.setSlot(2, confirmBtn);
68 | return super.open();
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/core/ChatManager.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.core;
2 |
3 | import io.icker.factions.FactionsMod;
4 | import io.icker.factions.api.persistents.Faction;
5 | import io.icker.factions.api.persistents.User;
6 | import io.icker.factions.util.Message;
7 |
8 | import net.fabricmc.fabric.api.message.v1.ServerMessageDecoratorEvent;
9 | import net.minecraft.server.network.ServerPlayerEntity;
10 | import net.minecraft.text.Text;
11 | import net.minecraft.util.Formatting;
12 |
13 | import java.util.UUID;
14 |
15 | public class ChatManager {
16 | public static void register() {
17 | ServerMessageDecoratorEvent.EVENT.register(
18 | ServerMessageDecoratorEvent.CONTENT_PHASE,
19 | (sender, message) -> {
20 | if (sender != null && FactionsMod.CONFIG.DISPLAY.MODIFY_CHAT) {
21 | return ChatManager.handleMessage(sender, message.getString());
22 | }
23 | return message;
24 | });
25 | }
26 |
27 | public static Text handleMessage(ServerPlayerEntity sender, String message) {
28 | UUID id = sender.getUuid();
29 | User member = User.get(id);
30 |
31 | if (member.chat == User.ChatMode.GLOBAL) {
32 | if (member.isInFaction()) {
33 | return ChatManager.inFactionGlobal(sender, member.getFaction(), message);
34 | } else {
35 | return ChatManager.global(sender, message);
36 | }
37 | } else {
38 | if (member.isInFaction()) {
39 | return ChatManager.faction(sender, member.getFaction(), message);
40 | } else {
41 | return ChatManager.global(sender, message);
42 | }
43 | }
44 | }
45 |
46 | private static Text global(ServerPlayerEntity sender, String message) {
47 | return new Message(message).format(Formatting.GRAY).raw();
48 | }
49 |
50 | private static Text inFactionGlobal(
51 | ServerPlayerEntity sender, Faction faction, String message) {
52 | return new Message()
53 | .add(new Message(faction.getName()).format(Formatting.BOLD, faction.getColor()))
54 | .filler("»")
55 | .add(new Message(message).format(Formatting.GRAY))
56 | .raw();
57 | }
58 |
59 | private static Text faction(ServerPlayerEntity sender, Faction faction, String message) {
60 | return new Message()
61 | .add(
62 | new Message(Text.translatable("factions.chat.in_faction_symbol"))
63 | .format(Formatting.BOLD, faction.getColor()))
64 | .filler("»")
65 | .add(new Message(message).format(Formatting.GRAY))
66 | .raw();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |

4 |
5 | # Factions Mod
6 |
7 | Highly customizable, lightweight and elegant factions mod for the [Fabric loader][fabric] in Minecraft 1.19
8 |
9 | [][github:releases]
10 | [][modrinth]
11 | [][modrinth:releases]
12 |
13 |
14 |
15 | ### **ABOUT**
16 |
17 | Factions Mod is an ultra lightweight, fast, and elegant solution to factions in modern minecraft. The **server-side** mod expands upon all the classic factions features whilst also focusing on customization and performance. Grow your faction, expand your claims, and storm your enemies for their chunks and loot.
18 |
19 | A faction's power cap increases as new members join, expanding their ability to claim more land. For each claim they make, it requires that faction to sustain more power. Dying to other players will temporarily lose faction power and if it drops below the required threshold, all their claims will be vulnerable to being overtaken.
20 |
21 |
22 |
23 | ### **FEATURES**
24 |
25 | - 🎯 Fully featured factions mod with over 30 [commands][wiki:commands]
26 | - ✨ Faction ranks, colors, MOTD and descriptions
27 | - 🎉 In faction private chat, global chat and a stylized player list
28 | - ⚡ Extreme performance and reliability
29 | - ⚙️ Advanced [configuration][wiki:config] and customization options
30 | - 🔥 Dynmap and Lucko Perms support out the box
31 | - 🚀 Event driven API for further extensibility
32 | - 💬 Strong [community][discord] and active developer support
33 |
34 |
35 |
36 | ### **GET STARTED**
37 |
38 | Factions Mod is very intuitive and works immediately after installation, requiring no additional configuration. However, you can read further about the mod on the [Wiki][wiki]. Our wiki goes in depth about the factions mechanics, its configuration, commands and integrations.
39 |
40 | A list of all **commands** is available on our [wiki][wiki:commands]
41 |
42 | Have an issue or a suggestion? Join [our discord][discord]
43 |
44 | ### **License**
45 | [MIT](LICENSE)
46 |
47 | [fabric]: https://fabricmc.net/
48 | [modrinth]: https://modrinth.com/mod/factions
49 | [modrinth:releases]: https://modrinth.com/mod/factions/versions
50 | [github:releases]: https://github.com/ickerio/factions/releases
51 | [wiki]: https://github.com/ickerio/factions/wiki
52 | [wiki:config]: https://github.com/ickerio/factions/wiki/Config
53 | [wiki:commands]: https://github.com/ickerio/factions/wiki/Commands
54 | [discord]: https://discord.gg/tHPFegeAY8
55 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/core/WorldManager.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.core;
2 |
3 | import io.icker.factions.FactionsMod;
4 | import io.icker.factions.api.events.MiscEvents;
5 | import io.icker.factions.api.events.PlayerEvents;
6 | import io.icker.factions.api.persistents.Claim;
7 | import io.icker.factions.api.persistents.Faction;
8 | import io.icker.factions.api.persistents.User;
9 | import io.icker.factions.util.Message;
10 |
11 | import net.minecraft.server.network.ServerPlayerEntity;
12 | import net.minecraft.server.world.ServerWorld;
13 | import net.minecraft.text.Text;
14 | import net.minecraft.util.Formatting;
15 | import net.minecraft.util.math.ChunkPos;
16 |
17 | public class WorldManager {
18 | public static void register() {
19 | PlayerEvents.ON_MOVE.register(WorldManager::onMove);
20 | MiscEvents.ON_MOB_SPAWN_ATTEMPT.register(WorldManager::onMobSpawnAttempt);
21 | }
22 |
23 | private static void onMobSpawnAttempt() {
24 | // TODO Implement this
25 | }
26 |
27 | private static void onMove(ServerPlayerEntity player) {
28 | User user = User.get(player.getUuid());
29 | ServerWorld world = (ServerWorld) player.getEntityWorld();
30 | String dimension = world.getRegistryKey().getValue().toString();
31 |
32 | ChunkPos chunkPos = world.getChunk(player.getBlockPos()).getPos();
33 |
34 | Claim claim = Claim.get(chunkPos.x, chunkPos.z, dimension);
35 | if (user.autoclaim && claim == null) {
36 | Faction faction = user.getFaction();
37 | int requiredPower =
38 | (faction.getClaims().size() + 1) * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT;
39 | int maxPower =
40 | faction.getUsers().size() * FactionsMod.CONFIG.POWER.MEMBER
41 | + FactionsMod.CONFIG.POWER.BASE
42 | + faction.getAdminPower();
43 |
44 | if (maxPower < requiredPower) {
45 | new Message(Text.translatable("factions.events.autoclaim.fail"))
46 | .fail()
47 | .send(player, false);
48 | user.autoclaim = false;
49 | } else {
50 | faction.addClaim(chunkPos.x, chunkPos.z, dimension);
51 | claim = Claim.get(chunkPos.x, chunkPos.z, dimension);
52 | new Message(
53 | Text.translatable(
54 | "factions.events.autoclaim.success",
55 | chunkPos.x,
56 | chunkPos.z,
57 | player.getName().getString()))
58 | .send(faction);
59 | }
60 | }
61 | if (user.radar) {
62 | if (claim != null) {
63 | new Message(claim.getFaction().getName())
64 | .format(claim.getFaction().getColor())
65 | .send(player, true);
66 | } else {
67 | new Message(Text.translatable("factions.radar.wilderness"))
68 | .format(Formatting.GREEN)
69 | .send(player, true);
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/command/JoinCommand.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.command;
2 |
3 | import com.mojang.brigadier.arguments.StringArgumentType;
4 | import com.mojang.brigadier.context.CommandContext;
5 | import com.mojang.brigadier.exceptions.CommandSyntaxException;
6 | import com.mojang.brigadier.tree.LiteralCommandNode;
7 |
8 | import io.icker.factions.FactionsMod;
9 | import io.icker.factions.api.persistents.Faction;
10 | import io.icker.factions.api.persistents.User;
11 | import io.icker.factions.util.Command;
12 | import io.icker.factions.util.Message;
13 |
14 | import net.minecraft.server.command.CommandManager;
15 | import net.minecraft.server.command.ServerCommandSource;
16 | import net.minecraft.server.network.ServerPlayerEntity;
17 | import net.minecraft.text.Text;
18 |
19 | public class JoinCommand implements Command {
20 | private int run(CommandContext context) throws CommandSyntaxException {
21 | String name = StringArgumentType.getString(context, "name");
22 | ServerCommandSource source = context.getSource();
23 | ServerPlayerEntity player = source.getPlayerOrThrow();
24 |
25 | Faction faction = Faction.getByName(name);
26 |
27 | if (faction == null) {
28 | new Message(Text.translatable("factions.command.join.fail.nonexistent_faction"))
29 | .fail()
30 | .send(player, false);
31 | return 0;
32 | }
33 |
34 | boolean invited = faction.isInvited(player.getUuid());
35 |
36 | if (!faction.isOpen() && !invited) {
37 | new Message(Text.translatable("factions.command.join.fail.private_no_invite"))
38 | .fail()
39 | .send(player, false);
40 | return 0;
41 | }
42 |
43 | if (FactionsMod.CONFIG.MAX_FACTION_SIZE != -1
44 | && faction.getUsers().size() >= FactionsMod.CONFIG.MAX_FACTION_SIZE) {
45 | new Message(Text.translatable("factions.command.join.fail.faction_full"))
46 | .fail()
47 | .send(player, false);
48 | return 0;
49 | }
50 |
51 | if (invited) faction.invites.remove(player.getUuid());
52 | Command.getUser(player).joinFaction(faction.getID(), User.Rank.MEMBER);
53 | source.getServer().getPlayerManager().sendCommandTree(player);
54 |
55 | new Message(
56 | Text.translatable(
57 | "factions.command.join.success", player.getName().getString()))
58 | .send(faction);
59 | faction.adjustPower(FactionsMod.CONFIG.POWER.MEMBER);
60 | return 1;
61 | }
62 |
63 | public LiteralCommandNode getNode() {
64 | return CommandManager.literal("join")
65 | .requires(
66 | Requires.multiple(
67 | Requires.isFactionless(), Requires.hasPerms("factions.join", 0)))
68 | .then(
69 | CommandManager.argument("name", StringArgumentType.greedyString())
70 | .suggests(Suggests.openInvitedFactions())
71 | .executes(this::run))
72 | .build();
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/command/CreateCommand.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.command;
2 |
3 | import com.mojang.brigadier.arguments.StringArgumentType;
4 | import com.mojang.brigadier.context.CommandContext;
5 | import com.mojang.brigadier.exceptions.CommandSyntaxException;
6 | import com.mojang.brigadier.tree.LiteralCommandNode;
7 |
8 | import io.icker.factions.FactionsMod;
9 | import io.icker.factions.api.persistents.Faction;
10 | import io.icker.factions.api.persistents.User;
11 | import io.icker.factions.util.Command;
12 | import io.icker.factions.util.Message;
13 |
14 | import net.minecraft.server.command.CommandManager;
15 | import net.minecraft.server.command.ServerCommandSource;
16 | import net.minecraft.server.network.ServerPlayerEntity;
17 | import net.minecraft.text.Text;
18 | import net.minecraft.util.Formatting;
19 |
20 | import xyz.nucleoid.server.translations.api.Localization;
21 |
22 | import java.util.Locale;
23 |
24 | public class CreateCommand implements Command {
25 | private int run(CommandContext context) throws CommandSyntaxException {
26 | String name = StringArgumentType.getString(context, "name");
27 |
28 | ServerCommandSource source = context.getSource();
29 | ServerPlayerEntity player = source.getPlayerOrThrow();
30 |
31 | if (FactionsMod.CONFIG.DISPLAY.NAME_BLACKLIST.contains(name.toLowerCase(Locale.ROOT))) {
32 | new Message(Text.translatable("factions.command.create.fail.blacklisted_name"))
33 | .fail()
34 | .send(player, false);
35 | return 0;
36 | }
37 |
38 | if (FactionsMod.CONFIG.DISPLAY.NAME_MAX_LENGTH > 0
39 | && FactionsMod.CONFIG.DISPLAY.NAME_MAX_LENGTH < name.length()) {
40 | new Message(Text.translatable("factions.command.create.fail.name_too_long"))
41 | .fail()
42 | .send(player, false);
43 | return 0;
44 | }
45 |
46 | if (Faction.getByName(name) != null) {
47 | new Message(Text.translatable("factions.command.create.fail.name_taken"))
48 | .fail()
49 | .send(player, false);
50 | return 0;
51 | }
52 |
53 | Faction faction =
54 | new Faction(
55 | name,
56 | Localization.raw("factions.default_description", player),
57 | Localization.raw("factions.default_motd", player),
58 | Formatting.WHITE,
59 | false,
60 | FactionsMod.CONFIG.POWER.BASE + FactionsMod.CONFIG.POWER.MEMBER);
61 | Faction.add(faction);
62 | Command.getUser(player).joinFaction(faction.getID(), User.Rank.OWNER);
63 |
64 | source.getServer().getPlayerManager().sendCommandTree(player);
65 | new Message(Text.translatable("factions.command.create.success")).send(player, false);
66 | return 1;
67 | }
68 |
69 | public LiteralCommandNode getNode() {
70 | return CommandManager.literal("create")
71 | .requires(
72 | Requires.multiple(
73 | Requires.isFactionless(), Requires.hasPerms("factions.create", 0)))
74 | .then(
75 | CommandManager.argument("name", StringArgumentType.greedyString())
76 | .executes(this::run))
77 | .build();
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/command/DisbandCommand.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.command;
2 |
3 | import com.mojang.brigadier.context.CommandContext;
4 | import com.mojang.brigadier.exceptions.CommandSyntaxException;
5 | import com.mojang.brigadier.tree.LiteralCommandNode;
6 |
7 | import io.icker.factions.api.persistents.Faction;
8 | import io.icker.factions.api.persistents.User;
9 | import io.icker.factions.util.Command;
10 | import io.icker.factions.util.Message;
11 |
12 | import net.minecraft.item.ItemStack;
13 | import net.minecraft.server.PlayerManager;
14 | import net.minecraft.server.command.CommandManager;
15 | import net.minecraft.server.command.ServerCommandSource;
16 | import net.minecraft.server.network.ServerPlayerEntity;
17 | import net.minecraft.text.Text;
18 | import net.minecraft.util.Formatting;
19 | import net.minecraft.util.ItemScatterer;
20 | import net.minecraft.util.collection.DefaultedList;
21 |
22 | public class DisbandCommand implements Command {
23 | private int run(CommandContext context, boolean confirm)
24 | throws CommandSyntaxException {
25 | ServerCommandSource source = context.getSource();
26 | ServerPlayerEntity player = source.getPlayerOrThrow();
27 |
28 | if (player == null) {
29 | return 0;
30 | }
31 |
32 | User user = Command.getUser(player);
33 | Faction faction = user.getFaction();
34 | if (faction == null) return 0;
35 |
36 | if (!faction.getSafe().isEmpty() && !confirm) {
37 | new Message(Text.translatable("factions.command.disband.fail.safe_not_empty"))
38 | .add(
39 | new Message(
40 | Text.translatable(
41 | "factions.command.disband.fail.safe_not_empty.prompt"))
42 | .hover(
43 | Text.translatable(
44 | "factions.command.disband.fail.safe_not_empty.prompt.hover"))
45 | .click("/f disband confirm")
46 | .format(Formatting.GREEN))
47 | .send(player, false);
48 | return 0;
49 | }
50 |
51 | DefaultedList safe = faction.clearSafe();
52 |
53 | ItemScatterer.spawn(player.getEntityWorld(), player.getBlockPos(), safe);
54 |
55 | new Message(
56 | Text.translatable(
57 | "factions.command.disband.success", player.getName().getString()))
58 | .send(faction);
59 | faction.remove();
60 |
61 | PlayerManager manager = source.getServer().getPlayerManager();
62 | for (ServerPlayerEntity p : manager.getPlayerList()) {
63 | manager.sendCommandTree(p);
64 | }
65 | return 1;
66 | }
67 |
68 | @Override
69 | public LiteralCommandNode getNode() {
70 | return CommandManager.literal("disband")
71 | .requires(
72 | Requires.multiple(
73 | Requires.isOwner(), Requires.hasPerms("factions.disband", 0)))
74 | .executes(context -> this.run(context, false))
75 | .then(
76 | CommandManager.literal("confirm")
77 | .executes(context -> this.run(context, true)))
78 | .build();
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/mixin/ServerPlayNetworkHandlerMixin.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.mixin;
2 |
3 | import io.icker.factions.api.events.PlayerEvents;
4 | import io.icker.factions.api.persistents.User;
5 | import io.icker.factions.util.Message;
6 | import io.icker.factions.util.WorldUtils;
7 |
8 | import net.minecraft.entity.Entity;
9 | import net.minecraft.network.message.SignedMessage;
10 | import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket;
11 | import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
12 | import net.minecraft.server.network.ServerPlayNetworkHandler;
13 | import net.minecraft.server.network.ServerPlayerEntity;
14 | import net.minecraft.server.world.ServerWorld;
15 | import net.minecraft.text.Text;
16 | import net.minecraft.util.ActionResult;
17 | import net.minecraft.util.Hand;
18 | import net.minecraft.util.math.Vec3d;
19 | import net.minecraft.world.World;
20 |
21 | import org.spongepowered.asm.mixin.Mixin;
22 | import org.spongepowered.asm.mixin.Shadow;
23 | import org.spongepowered.asm.mixin.injection.At;
24 | import org.spongepowered.asm.mixin.injection.Inject;
25 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
26 |
27 | @Mixin(ServerPlayNetworkHandler.class)
28 | public class ServerPlayNetworkHandlerMixin {
29 | @Shadow public ServerPlayerEntity player;
30 |
31 | @Inject(method = "onPlayerMove", at = @At("HEAD"))
32 | public void onPlayerMove(PlayerMoveC2SPacket packet, CallbackInfo ci) {
33 | PlayerEvents.ON_MOVE.invoker().onMove(player);
34 | }
35 |
36 | @Inject(method = "handleDecoratedMessage", at = @At("HEAD"), cancellable = true)
37 | public void handleDecoratedMessage(SignedMessage signedMessage, CallbackInfo ci) {
38 | User member = User.get(signedMessage.link().sender());
39 |
40 | boolean factionChat =
41 | member.chat == User.ChatMode.FACTION || member.chat == User.ChatMode.FOCUS;
42 |
43 | if (factionChat && !member.isInFaction()) {
44 | new Message(Text.translatable("factions.chat.faction_chat_when_not_in_faction"))
45 | .fail()
46 | .hover(
47 | Text.translatable(
48 | "factions.chat.faction_chat_when_not_in_faction.hover"))
49 | .click("/factions settings chat global")
50 | .send(
51 | WorldUtils.server
52 | .getPlayerManager()
53 | .getPlayer(signedMessage.link().sender()),
54 | false);
55 |
56 | ci.cancel();
57 | }
58 | }
59 |
60 | @Inject(method = "onPlayerInteractEntity", at = @At("HEAD"), cancellable = true)
61 | public void onPlayerInteractEntity(PlayerInteractEntityC2SPacket packet, CallbackInfo ci) {
62 | World world = player.getEntityWorld();
63 | Entity entity = packet.getEntity((ServerWorld) world);
64 | if (entity == null) return;
65 |
66 | packet.handle(
67 | new PlayerInteractEntityC2SPacket.Handler() {
68 | @Override
69 | public void interact(Hand hand) {
70 | if (PlayerEvents.USE_ENTITY.invoker().onUseEntity(player, entity, world)
71 | == ActionResult.FAIL) {
72 | ci.cancel();
73 | }
74 | }
75 |
76 | @Override
77 | public void interactAt(Hand hand, Vec3d pos) {
78 | if (PlayerEvents.USE_ENTITY.invoker().onUseEntity(player, entity, world)
79 | == ActionResult.FAIL) {
80 | ci.cancel();
81 | }
82 | }
83 |
84 | @Override
85 | public void attack() {}
86 | });
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/command/MapCommand.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.command;
2 |
3 | import com.mojang.brigadier.context.CommandContext;
4 | import com.mojang.brigadier.exceptions.CommandSyntaxException;
5 | import com.mojang.brigadier.tree.LiteralCommandNode;
6 |
7 | import io.icker.factions.api.persistents.Claim;
8 | import io.icker.factions.api.persistents.Faction;
9 | import io.icker.factions.util.Command;
10 | import io.icker.factions.util.Message;
11 |
12 | import net.minecraft.server.command.CommandManager;
13 | import net.minecraft.server.command.ServerCommandSource;
14 | import net.minecraft.server.network.ServerPlayerEntity;
15 | import net.minecraft.server.world.ServerWorld;
16 | import net.minecraft.text.Text;
17 | import net.minecraft.util.Formatting;
18 | import net.minecraft.util.math.ChunkPos;
19 |
20 | public class MapCommand implements Command {
21 | private int run(CommandContext context) throws CommandSyntaxException {
22 | ServerCommandSource source = context.getSource();
23 |
24 | ServerPlayerEntity player = source.getPlayerOrThrow();
25 | ServerWorld world = (ServerWorld) player.getEntityWorld();
26 |
27 | ChunkPos chunkPos = world.getChunk(player.getBlockPos()).getPos();
28 | String dimension = world.getRegistryKey().getValue().toString();
29 |
30 | // Print the header of the faction map.
31 | new Message(
32 | Text.literal("──┤ ")
33 | .formatted(Formatting.DARK_GRAY)
34 | .append(
35 | Text.translatable("factions.command.map.title")
36 | .formatted(Formatting.GREEN))
37 | .append(Text.literal("├──").formatted(Formatting.DARK_GRAY)))
38 | .send(player, false);
39 |
40 | for (int z = -4; z <= 4; z++) { // Rows (9)
41 | Message row = new Message();
42 | for (int x = -5; x <= 5; x++) { // Columns (11)
43 | Claim claim = Claim.get(chunkPos.x + x, chunkPos.z + z, dimension);
44 | if (x == 0 && z == 0) { // Check if middle (your chunk)
45 | if (claim == null) {
46 | row.add(
47 | new Message("⏺")
48 | .format(Formatting.DARK_GRAY)
49 | .hover(
50 | Text.translatable(
51 | "factions.command.map.you_wilderness")));
52 | } else {
53 | Faction owner = claim.getFaction();
54 | row.add(
55 | new Message("⏺")
56 | .format(owner.getColor())
57 | .hover(
58 | Text.translatable(
59 | "factions.command.map.you_owner",
60 | owner.getName())));
61 | }
62 | } else {
63 | if (claim == null) {
64 | row.add("□").format(Formatting.DARK_GRAY);
65 | } else {
66 | Faction owner = claim.getFaction();
67 | row.add(new Message("■").format(owner.getColor()).hover(owner.getName()));
68 | }
69 | }
70 | row.add(" ");
71 | }
72 | row.send(player, false);
73 | }
74 |
75 | return 1;
76 | }
77 |
78 | @Override
79 | public LiteralCommandNode getNode() {
80 | return CommandManager.literal("map")
81 | .requires(Requires.hasPerms("factions.map", 0))
82 | .executes(this::run)
83 | .build();
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/util/Message.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.util;
2 |
3 | import io.icker.factions.api.persistents.Faction;
4 | import io.icker.factions.api.persistents.User;
5 |
6 | import net.minecraft.entity.player.PlayerEntity;
7 | import net.minecraft.server.PlayerManager;
8 | import net.minecraft.server.network.ServerPlayerEntity;
9 | import net.minecraft.text.ClickEvent.RunCommand;
10 | import net.minecraft.text.HoverEvent.ShowText;
11 | import net.minecraft.text.MutableText;
12 | import net.minecraft.text.Text;
13 | import net.minecraft.util.Formatting;
14 |
15 | public class Message {
16 | public static PlayerManager manager;
17 | private MutableText text;
18 |
19 | public Message() {
20 | text = Text.literal("");
21 | }
22 |
23 | public Message(String message) {
24 | text = (MutableText) Text.of(message);
25 | }
26 |
27 | public Message(String message, Object... args) {
28 | text = (MutableText) Text.of(String.format(message, args));
29 | }
30 |
31 | public Message(MutableText text) {
32 | this.text = text;
33 | }
34 |
35 | public Message add(String message) {
36 | text.append(message);
37 | return this;
38 | }
39 |
40 | public Message add(String message, Object... args) {
41 | text.append(String.format(message, args));
42 | return this;
43 | }
44 |
45 | public Message add(Message message) {
46 | text.append(message.raw());
47 | return this;
48 | }
49 |
50 | public Message format(Formatting... format) {
51 | text.formatted(format);
52 | return this;
53 | }
54 |
55 | public Message fail() {
56 | text.formatted(Formatting.RED);
57 | return this;
58 | }
59 |
60 | public Message hover(String message) {
61 | return this.hover(Text.of(message));
62 | }
63 |
64 | public Message hover(Text message) {
65 | text.styled(s -> s.withHoverEvent(new ShowText(message)));
66 | return this;
67 | }
68 |
69 | public Message click(String message) {
70 | text.styled(s -> s.withClickEvent(new RunCommand(message)));
71 | return this;
72 | }
73 |
74 | public Message send(PlayerEntity player, boolean actionBar) {
75 | player.sendMessage(text, actionBar);
76 | return this;
77 | }
78 |
79 | public Message send(Faction faction) {
80 | Message message = this.prependFaction(faction);
81 | for (User member : faction.getUsers()) {
82 | ServerPlayerEntity player = manager.getPlayer(member.getID());
83 | if (player != null) message.send(player, false);
84 | }
85 | return this;
86 | }
87 |
88 | public void sendToGlobalChat() {
89 | for (ServerPlayerEntity player : manager.getPlayerList()) {
90 | User.ChatMode option = User.get(player.getUuid()).chat;
91 | if (option != User.ChatMode.FOCUS) player.sendMessage(text, false);
92 | }
93 | }
94 |
95 | public void sendToFactionChat(Faction faction) {
96 | for (User member : faction.getUsers()) {
97 | ServerPlayerEntity player = manager.getPlayer(member.getID());
98 | player.sendMessage(text, false);
99 | }
100 | }
101 |
102 | public Message prependFaction(Faction faction) {
103 | text =
104 | new Message()
105 | .add(
106 | new Message(
107 | faction.getColor().toString()
108 | + Formatting.BOLD
109 | + faction.getName())
110 | .hover(faction.getDescription()))
111 | .filler("»")
112 | .raw()
113 | .append(text);
114 | return this;
115 | }
116 |
117 | public Message filler(String symbol) {
118 | text.append(
119 | Text.of(
120 | " "
121 | + Formatting.RESET
122 | + Formatting.DARK_GRAY
123 | + symbol
124 | + Formatting.RESET
125 | + " "));
126 | return this;
127 | }
128 |
129 | public MutableText raw() {
130 | return text;
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/mixin/ServerPlayerEntityMixin.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.mixin;
2 |
3 | import io.icker.factions.FactionsMod;
4 | import io.icker.factions.api.events.PlayerEvents;
5 | import io.icker.factions.api.persistents.Faction;
6 | import io.icker.factions.api.persistents.User;
7 | import io.icker.factions.util.Message;
8 |
9 | import net.minecraft.entity.Entity;
10 | import net.minecraft.entity.EntityType;
11 | import net.minecraft.entity.LivingEntity;
12 | import net.minecraft.entity.damage.DamageSource;
13 | import net.minecraft.server.network.ServerPlayerEntity;
14 | import net.minecraft.server.world.ServerWorld;
15 | import net.minecraft.text.Text;
16 | import net.minecraft.util.ActionResult;
17 | import net.minecraft.util.Formatting;
18 | import net.minecraft.world.World;
19 |
20 | import org.spongepowered.asm.mixin.Mixin;
21 | import org.spongepowered.asm.mixin.injection.At;
22 | import org.spongepowered.asm.mixin.injection.Inject;
23 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
24 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
25 |
26 | @Mixin(ServerPlayerEntity.class)
27 | public abstract class ServerPlayerEntityMixin extends LivingEntity {
28 |
29 | protected ServerPlayerEntityMixin(EntityType extends LivingEntity> entityType, World world) {
30 | super(entityType, world);
31 | }
32 |
33 | @Inject(at = @At("HEAD"), method = "onDeath")
34 | public void onDeath(DamageSource source, CallbackInfo info) {
35 | Entity entity = source.getSource();
36 | if (entity == null || !entity.isPlayer()) return;
37 | PlayerEvents.ON_KILLED_BY_PLAYER
38 | .invoker()
39 | .onKilledByPlayer((ServerPlayerEntity) (Object) this, source);
40 | }
41 |
42 | @Inject(at = @At("HEAD"), method = "tick")
43 | public void tick(CallbackInfo info) {
44 | if (age % FactionsMod.CONFIG.POWER.POWER_TICKS.TICKS != 0 || age == 0) return;
45 | PlayerEvents.ON_POWER_TICK.invoker().onPowerTick((ServerPlayerEntity) (Object) this);
46 | }
47 |
48 | @Inject(method = "isInvulnerableTo", at = @At("RETURN"), cancellable = true)
49 | public void isInvulnerableTo(
50 | ServerWorld world, DamageSource damageSource, CallbackInfoReturnable info) {
51 | Entity source = damageSource.getAttacker();
52 | if (source == null) return;
53 |
54 | ActionResult result =
55 | PlayerEvents.IS_INVULNERABLE
56 | .invoker()
57 | .isInvulnerable(
58 | damageSource.getAttacker(), (ServerPlayerEntity) (Object) this);
59 |
60 | if (result != ActionResult.PASS) info.setReturnValue(result == ActionResult.SUCCESS);
61 | }
62 |
63 | @Inject(method = "getPlayerListName", at = @At("HEAD"), cancellable = true)
64 | public void getPlayerListName(CallbackInfoReturnable cir) {
65 | if (FactionsMod.CONFIG.DISPLAY.TAB_MENU) {
66 | User member = User.get(((ServerPlayerEntity) (Object) this).getUuid());
67 | if (member.isInFaction()) {
68 | Faction faction = member.getFaction();
69 | cir.setReturnValue(
70 | new Message(String.format("[%s] ", faction.getName()))
71 | .format(faction.getColor())
72 | .add(
73 | new Message(
74 | ((ServerPlayerEntity) (Object) this)
75 | .getName()
76 | .getString())
77 | .format(Formatting.WHITE))
78 | .raw());
79 | } else {
80 | cir.setReturnValue(
81 | new Message(Text.translatable("factions.factionless"))
82 | .format(Formatting.GRAY)
83 | .add(
84 | new Message(
85 | ((ServerPlayerEntity) (Object) this)
86 | .getName()
87 | .getString())
88 | .format(Formatting.WHITE))
89 | .raw());
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/ui/ListGui.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.ui;
2 |
3 | import eu.pb4.sgui.api.ClickType;
4 | import eu.pb4.sgui.api.elements.GuiElementBuilder;
5 |
6 | import io.icker.factions.api.persistents.Faction;
7 | import io.icker.factions.api.persistents.Home;
8 | import io.icker.factions.api.persistents.User;
9 | import io.icker.factions.command.HomeCommand;
10 | import io.icker.factions.util.GuiInteract;
11 | import io.icker.factions.util.Icons;
12 |
13 | import net.minecraft.item.Items;
14 | import net.minecraft.server.network.ServerPlayerEntity;
15 | import net.minecraft.text.Style;
16 | import net.minecraft.text.Text;
17 | import net.minecraft.util.Formatting;
18 |
19 | import org.jetbrains.annotations.Nullable;
20 |
21 | import java.util.ArrayList;
22 | import java.util.List;
23 |
24 | public class ListGui extends PagedGui {
25 | List factions;
26 | int size;
27 | User user;
28 |
29 | public ListGui(ServerPlayerEntity player, User user, @Nullable Runnable closeCallback) {
30 | super(player, closeCallback);
31 | this.user = user;
32 |
33 | this.factions = new ArrayList<>(Faction.all().stream().toList());
34 | Faction userFaction;
35 | if ((userFaction = user.getFaction()) != null) {
36 | this.factions.remove(userFaction);
37 | this.factions.addFirst(userFaction);
38 | }
39 | this.size = factions.size();
40 |
41 | this.setTitle(Text.translatable("factions.gui.list.title"));
42 | this.updateDisplay();
43 | this.open();
44 | }
45 |
46 | @Override
47 | protected int getPageAmount() {
48 | return this.size / PAGE_SIZE;
49 | }
50 |
51 | @Override
52 | protected DisplayElement getElement(int id) {
53 | if (this.size > id) {
54 | var faction = this.factions.get(id);
55 |
56 | boolean isInFaction = faction.equals(this.user.getFaction());
57 | Home home = faction.getHome();
58 |
59 | var icon = new GuiElementBuilder(Items.PLAYER_HEAD);
60 | icon.setProfileSkinTexture(
61 | isInFaction ? Icons.GUI_CASTLE_NORMAL : Icons.GUI_CASTLE_OPEN);
62 | icon.setName(Text.literal(faction.getColor() + faction.getName()));
63 |
64 | List lore =
65 | new ArrayList<>(
66 | List.of(
67 | Text.literal(faction.getDescription())
68 | .setStyle(
69 | Style.EMPTY
70 | .withItalic(false)
71 | .withColor(Formatting.GRAY))));
72 | if (isInFaction && home != null) {
73 | lore.add(
74 | Text.translatable("factions.gui.list.entry.view_info")
75 | .setStyle(
76 | Style.EMPTY.withItalic(false).withColor(Formatting.GRAY)));
77 | lore.add(
78 | Text.translatable("factions.gui.list.entry.teleport")
79 | .setStyle(
80 | Style.EMPTY
81 | .withItalic(false)
82 | .withColor(Formatting.DARK_AQUA)));
83 | icon.setCallback(
84 | (index, clickType, actionType) -> {
85 | GuiInteract.playClickSound(player);
86 | if (clickType == ClickType.MOUSE_RIGHT) {
87 | new HomeCommand().execGo(player, user, faction);
88 | this.close();
89 | return;
90 | }
91 | new InfoGui(player, faction, this::open);
92 | });
93 | } else {
94 | lore.add(
95 | Text.translatable("factions.gui.list.entry.view_info")
96 | .setStyle(
97 | Style.EMPTY.withItalic(false).withColor(Formatting.GRAY)));
98 | icon.setCallback(
99 | (index, clickType, actionType) -> {
100 | GuiInteract.playClickSound(player);
101 | new InfoGui(player, faction, this::open);
102 | });
103 | }
104 | icon.setLore(lore);
105 |
106 | return DisplayElement.of(icon);
107 | }
108 |
109 | return DisplayElement.empty();
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/command/KickCommand.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.command;
2 |
3 | import com.mojang.authlib.GameProfile;
4 | import com.mojang.brigadier.arguments.StringArgumentType;
5 | import com.mojang.brigadier.context.CommandContext;
6 | import com.mojang.brigadier.exceptions.CommandSyntaxException;
7 | import com.mojang.brigadier.tree.LiteralCommandNode;
8 |
9 | import io.icker.factions.api.persistents.User;
10 | import io.icker.factions.util.Command;
11 | import io.icker.factions.util.Message;
12 |
13 | import net.minecraft.server.command.CommandManager;
14 | import net.minecraft.server.command.ServerCommandSource;
15 | import net.minecraft.server.network.ServerPlayerEntity;
16 | import net.minecraft.text.Text;
17 | import net.minecraft.util.Formatting;
18 |
19 | import xyz.nucleoid.server.translations.api.Localization;
20 |
21 | import java.util.Optional;
22 | import java.util.UUID;
23 |
24 | public class KickCommand implements Command {
25 | private int run(CommandContext context) throws CommandSyntaxException {
26 | ServerCommandSource source = context.getSource();
27 | ServerPlayerEntity player = source.getPlayerOrThrow();
28 |
29 | String name = StringArgumentType.getString(context, "player");
30 |
31 | User target;
32 |
33 | Optional profile;
34 | if ((profile = source.getServer().getApiServices().profileResolver().getProfileByName(name))
35 | .isPresent()) {
36 | target = User.get(profile.get().id());
37 | } else {
38 | try {
39 | target = User.get(UUID.fromString(name));
40 | } catch (Exception e) {
41 | new Message(Text.translatable("factions.gui.spoof.fail.no_player", name))
42 | .format(Formatting.RED)
43 | .send(player, false);
44 | return 0;
45 | }
46 | }
47 |
48 | if (target.getID().equals(player.getUuid())) {
49 | new Message(Text.translatable("factions.command.kick.fail.self"))
50 | .fail()
51 | .send(player, false);
52 | return 0;
53 | }
54 |
55 | User selfUser = Command.getUser(player);
56 |
57 | if (target.getFaction() == null || !target.getFaction().equals(selfUser.getFaction())) {
58 | new Message(Text.translatable("factions.command.kick.fail.other_faction"))
59 | .fail()
60 | .send(player, false);
61 | return 0;
62 | }
63 |
64 | if (selfUser.rank == User.Rank.LEADER
65 | && (target.rank == User.Rank.LEADER || target.rank == User.Rank.OWNER)) {
66 | new Message(Text.translatable("factions.command.kick.fail.high_rank"))
67 | .fail()
68 | .send(player, false);
69 | return 0;
70 | }
71 |
72 | ServerPlayerEntity targetPlayer =
73 | player.getEntityWorld().getServer().getPlayerManager().getPlayer(target.getID());
74 |
75 | target.leaveFaction();
76 |
77 | if (targetPlayer != null) {
78 | context.getSource().getServer().getPlayerManager().sendCommandTree(targetPlayer);
79 |
80 | new Message(
81 | Text.translatable(
82 | "factions.command.kick.success.subject",
83 | player.getName().getString()))
84 | .send(targetPlayer, false);
85 | }
86 |
87 | new Message(
88 | Text.translatable(
89 | "factions.command.kick.success.actor",
90 | profile.map((found_profile) -> found_profile.name())
91 | .orElse(
92 | Localization.raw(
93 | "factions.gui.members.entry.unknown_player",
94 | player))))
95 | .send(player, false);
96 |
97 | return 1;
98 | }
99 |
100 | public LiteralCommandNode getNode() {
101 | return CommandManager.literal("kick")
102 | .requires(
103 | Requires.multiple(
104 | Requires.isLeader(), Requires.hasPerms("factions.kick", 0)))
105 | .then(
106 | CommandManager.argument("player", StringArgumentType.string())
107 | .suggests(Suggests.allPlayersInYourFactionButYou())
108 | .executes(this::run))
109 | .build();
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/api/persistents/User.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.api.persistents;
2 |
3 | import io.icker.factions.api.events.FactionEvents;
4 | import io.icker.factions.database.Database;
5 | import io.icker.factions.database.Field;
6 | import io.icker.factions.database.Name;
7 | import io.icker.factions.util.WorldUtils;
8 |
9 | import net.minecraft.server.network.ServerPlayerEntity;
10 |
11 | import org.jetbrains.annotations.NotNull;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | import java.util.Collection;
15 | import java.util.HashMap;
16 | import java.util.List;
17 | import java.util.UUID;
18 |
19 | @Name("User")
20 | public class User {
21 | private static final HashMap STORE = Database.load(User.class, User::getID);
22 |
23 | public enum ChatMode {
24 | FOCUS,
25 | FACTION,
26 | GLOBAL
27 | }
28 |
29 | public enum Rank {
30 | OWNER,
31 | LEADER,
32 | COMMANDER,
33 | MEMBER,
34 | GUEST
35 | }
36 |
37 | public enum SoundMode {
38 | NONE,
39 | WARNINGS,
40 | FACTION,
41 | ALL
42 | }
43 |
44 | @Field("ID")
45 | private UUID id;
46 |
47 | @Field("FactionID")
48 | private UUID factionID;
49 |
50 | @Field("Rank")
51 | public Rank rank;
52 |
53 | @Field("Radar")
54 | public boolean radar = false;
55 |
56 | @Field("Chat")
57 | public ChatMode chat = ChatMode.GLOBAL;
58 |
59 | @Field("Sounds")
60 | public SoundMode sounds = SoundMode.ALL;
61 |
62 | @Field("HomeCooldown")
63 | public long homeCooldown = -1;
64 |
65 | public boolean autoclaim = false;
66 | public boolean bypass = false;
67 |
68 | private User spoof;
69 |
70 | public User(UUID id) {
71 | this.id = id;
72 | }
73 |
74 | public User() {}
75 |
76 | public String getKey() {
77 | return id.toString();
78 | }
79 |
80 | @NotNull
81 | public static User get(UUID id) {
82 | if (!STORE.containsKey(id)) {
83 | User.add(new User(id));
84 | }
85 | return STORE.get(id);
86 | }
87 |
88 | public static List getByFaction(UUID factionID) {
89 | return STORE.values().stream()
90 | .filter(m -> m.isInFaction() && m.factionID.equals(factionID))
91 | .toList();
92 | }
93 |
94 | public static void add(User user) {
95 | STORE.put(user.id, user);
96 | }
97 |
98 | public UUID getID() {
99 | return id;
100 | }
101 |
102 | public boolean isInFaction() {
103 | return factionID != null;
104 | }
105 |
106 | private String getEnumName(Enum> value) {
107 | return value.name().toLowerCase();
108 | }
109 |
110 | public String getRankName() {
111 | return getEnumName(rank);
112 | }
113 |
114 | public String getChatName() {
115 | return getEnumName(chat);
116 | }
117 |
118 | public String getSoundName() {
119 | return getEnumName(sounds);
120 | }
121 |
122 | @Nullable
123 | public Faction getFaction() {
124 | return Faction.get(factionID);
125 | }
126 |
127 | public User getSpoof() {
128 | return spoof;
129 | }
130 |
131 | public void setSpoof(User user) {
132 | this.spoof = user;
133 | }
134 |
135 | public void joinFaction(UUID factionID, Rank rank) {
136 | this.factionID = factionID;
137 | this.rank = rank;
138 | FactionEvents.MEMBER_JOIN.invoker().onMemberJoin(Faction.get(factionID), this);
139 | }
140 |
141 | public void leaveFaction() {
142 | UUID oldFactionID = factionID;
143 | factionID = null;
144 | rank = null;
145 | FactionEvents.MEMBER_LEAVE.invoker().onMemberLeave(Faction.get(oldFactionID), this);
146 | }
147 |
148 | public static Collection all() {
149 | return STORE.values();
150 | }
151 |
152 | public static void audit() {
153 | STORE.values()
154 | .forEach(
155 | (user) -> {
156 | if (Faction.get(user.factionID) == null) {
157 | user.factionID = null;
158 | }
159 |
160 | if (!user.isInFaction()) {
161 | user.rank = null;
162 | }
163 | });
164 | }
165 |
166 | @Nullable
167 | public String getLanguage() {
168 | ServerPlayerEntity player = WorldUtils.server.getPlayerManager().getPlayer(this.id);
169 |
170 | if (player == null) {
171 | return null;
172 | }
173 |
174 | return player.getClientOptions().language();
175 | }
176 |
177 | public static void save() {
178 | Database.save(User.class, STORE.values().stream().toList());
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/FactionsMod.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions;
2 |
3 | import com.mojang.brigadier.CommandDispatcher;
4 | import com.mojang.brigadier.tree.LiteralCommandNode;
5 |
6 | import io.icker.factions.command.AdminCommand;
7 | import io.icker.factions.command.ClaimCommand;
8 | import io.icker.factions.command.CreateCommand;
9 | import io.icker.factions.command.DeclareCommand;
10 | import io.icker.factions.command.DisbandCommand;
11 | import io.icker.factions.command.HomeCommand;
12 | import io.icker.factions.command.InfoCommand;
13 | import io.icker.factions.command.InviteCommand;
14 | import io.icker.factions.command.JoinCommand;
15 | import io.icker.factions.command.KickCommand;
16 | import io.icker.factions.command.LeaveCommand;
17 | import io.icker.factions.command.ListCommand;
18 | import io.icker.factions.command.MapCommand;
19 | import io.icker.factions.command.MemberCommand;
20 | import io.icker.factions.command.ModifyCommand;
21 | import io.icker.factions.command.PermissionCommand;
22 | import io.icker.factions.command.RankCommand;
23 | import io.icker.factions.command.SafeCommand;
24 | import io.icker.factions.command.SettingsCommand;
25 | import io.icker.factions.config.Config;
26 | import io.icker.factions.core.ChatManager;
27 | import io.icker.factions.core.FactionsManager;
28 | import io.icker.factions.core.InteractionManager;
29 | import io.icker.factions.core.ServerManager;
30 | import io.icker.factions.core.SoundManager;
31 | import io.icker.factions.core.WorldManager;
32 | import io.icker.factions.util.BlueMapWrapper;
33 | import io.icker.factions.util.Command;
34 | import io.icker.factions.util.DynmapWrapper;
35 | import io.icker.factions.util.PlaceholdersWrapper;
36 | import io.icker.factions.util.SquareMapWrapper;
37 | import io.icker.factions.util.WorldUtils;
38 |
39 | import net.fabricmc.api.ModInitializer;
40 | import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
41 | import net.fabricmc.loader.api.FabricLoader;
42 | import net.minecraft.command.CommandRegistryAccess;
43 | import net.minecraft.server.command.CommandManager;
44 | import net.minecraft.server.command.ServerCommandSource;
45 |
46 | import org.slf4j.Logger;
47 | import org.slf4j.LoggerFactory;
48 |
49 | public class FactionsMod implements ModInitializer {
50 | public static Logger LOGGER = LoggerFactory.getLogger("Factions");
51 | public static final String MODID = "factions";
52 |
53 | public static Config CONFIG = Config.load();
54 | public static DynmapWrapper dynmap;
55 | public static BlueMapWrapper bluemap;
56 | public static SquareMapWrapper squaremap;
57 |
58 | @Override
59 | public void onInitialize() {
60 | LOGGER.info("Initialized Factions Mod");
61 |
62 | WorldUtils.register();
63 |
64 | dynmap = FabricLoader.getInstance().isModLoaded("dynmap") ? new DynmapWrapper() : null;
65 | bluemap = FabricLoader.getInstance().isModLoaded("bluemap") ? new BlueMapWrapper() : null;
66 | squaremap =
67 | FabricLoader.getInstance().isModLoaded("squaremap") ? new SquareMapWrapper() : null;
68 |
69 | if (FabricLoader.getInstance().isModLoaded("placeholder-api")) PlaceholdersWrapper.init();
70 |
71 | ChatManager.register();
72 | FactionsManager.register();
73 | InteractionManager.register();
74 | ServerManager.register();
75 | SoundManager.register();
76 | WorldManager.register();
77 |
78 | CommandRegistrationCallback.EVENT.register(FactionsMod::registerCommands);
79 | }
80 |
81 | private static void registerCommands(
82 | CommandDispatcher dispatcher,
83 | CommandRegistryAccess registryAccess,
84 | CommandManager.RegistrationEnvironment environment) {
85 | LiteralCommandNode factions =
86 | CommandManager.literal("factions").build();
87 |
88 | LiteralCommandNode alias = CommandManager.literal("f").build();
89 |
90 | dispatcher.getRoot().addChild(factions);
91 | dispatcher.getRoot().addChild(alias);
92 |
93 | Command[] commands =
94 | new Command[] {
95 | new AdminCommand(),
96 | new SettingsCommand(),
97 | new ClaimCommand(),
98 | new CreateCommand(),
99 | new DeclareCommand(),
100 | new DisbandCommand(),
101 | new HomeCommand(),
102 | new InfoCommand(),
103 | new InviteCommand(),
104 | new JoinCommand(),
105 | new KickCommand(),
106 | new LeaveCommand(),
107 | new ListCommand(),
108 | new MapCommand(),
109 | new MemberCommand(),
110 | new ModifyCommand(),
111 | new RankCommand(),
112 | new SafeCommand(),
113 | new PermissionCommand()
114 | };
115 |
116 | for (Command command : commands) {
117 | factions.addChild(command.getNode());
118 | alias.addChild(command.getNode());
119 | }
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/config/Config.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.config;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.GsonBuilder;
5 | import com.google.gson.JsonDeserializationContext;
6 | import com.google.gson.JsonDeserializer;
7 | import com.google.gson.JsonElement;
8 | import com.google.gson.JsonParseException;
9 | import com.google.gson.annotations.SerializedName;
10 |
11 | import io.icker.factions.FactionsMod;
12 | import io.icker.factions.api.persistents.Relationship;
13 |
14 | import net.fabricmc.loader.api.FabricLoader;
15 |
16 | import org.jetbrains.annotations.Nullable;
17 |
18 | import java.io.File;
19 | import java.io.FileReader;
20 | import java.io.FileWriter;
21 | import java.lang.reflect.Type;
22 | import java.util.List;
23 |
24 | public class Config {
25 | private static final int REQUIRED_VERSION = 3;
26 | private static final File file =
27 | FabricLoader.getInstance()
28 | .getGameDir()
29 | .resolve("config")
30 | .resolve("factions.json")
31 | .toFile();
32 |
33 | public static Config load() {
34 | Gson gson =
35 | new GsonBuilder()
36 | .setPrettyPrinting()
37 | .disableHtmlEscaping()
38 | .serializeNulls()
39 | .registerTypeAdapter(HomeConfig.class, new Deserializer<>(HomeConfig.class))
40 | .registerTypeAdapter(
41 | PowerConfig.class, new Deserializer<>(PowerConfig.class))
42 | .registerTypeAdapter(SafeConfig.class, new Deserializer<>(SafeConfig.class))
43 | .create();
44 |
45 | try {
46 | if (!file.exists()) {
47 | file.getParentFile().mkdir();
48 |
49 | Config defaults = new Config();
50 |
51 | FileWriter writer = new FileWriter(file);
52 | gson.toJson(defaults, writer);
53 | writer.close();
54 |
55 | return defaults;
56 | }
57 |
58 | Config config = gson.fromJson(new FileReader(file), Config.class);
59 |
60 | if (config.VERSION != REQUIRED_VERSION) {
61 | FactionsMod.LOGGER.error(
62 | String.format(
63 | "Config file incompatible (requires version %d)",
64 | REQUIRED_VERSION));
65 | }
66 |
67 | return config;
68 | } catch (Exception e) {
69 | FactionsMod.LOGGER.error("An error occurred reading the factions config file", e);
70 | return new Config();
71 | }
72 | }
73 |
74 | @SerializedName("version")
75 | public int VERSION = REQUIRED_VERSION;
76 |
77 | @SerializedName("gui")
78 | public boolean GUI = true;
79 |
80 | @SerializedName("blockTNT")
81 | public boolean BLOCK_TNT = false;
82 |
83 | @SerializedName("power")
84 | public PowerConfig POWER = new PowerConfig();
85 |
86 | @SerializedName("safe")
87 | @Nullable
88 | public SafeConfig SAFE = new SafeConfig();
89 |
90 | @SerializedName("home")
91 | @Nullable
92 | public HomeConfig HOME = new HomeConfig();
93 |
94 | @SerializedName("display")
95 | public DisplayConfig DISPLAY = new DisplayConfig();
96 |
97 | @SerializedName("relationships")
98 | public RelationshipConfig RELATIONSHIPS = new RelationshipConfig();
99 |
100 | @SerializedName("maxFactionSize")
101 | public int MAX_FACTION_SIZE = -1;
102 |
103 | @SerializedName("friendlyFire")
104 | public boolean FRIENDLY_FIRE = false;
105 |
106 | @SerializedName("requiredBypassLevel")
107 | public int REQUIRED_BYPASS_LEVEL = 2;
108 |
109 | @SerializedName("claimProtections")
110 | public boolean CLAIM_PROTECTION = true;
111 |
112 | @SerializedName("language")
113 | public String LANGUAGE = "en_us";
114 |
115 | public static class DisplayConfig {
116 | @SerializedName("factionNameMaxLength")
117 | public int NAME_MAX_LENGTH = -1;
118 |
119 | @SerializedName("changeChat")
120 | public boolean MODIFY_CHAT = true;
121 |
122 | @SerializedName("tabMenu")
123 | public boolean TAB_MENU = true;
124 |
125 | @SerializedName("nameBlackList")
126 | public List NAME_BLACKLIST =
127 | List.of("wilderness", "factionless", "без фракции"); // means no faction in english
128 |
129 | @SerializedName("powerMessage")
130 | public boolean POWER_MESSAGE = true;
131 | }
132 |
133 | public static class RelationshipConfig {
134 | @SerializedName("allyOverridesPermissions")
135 | public boolean ALLY_OVERRIDES_PERMISSIONS = true;
136 |
137 | @SerializedName("defaultGuestPermissions")
138 | public List DEFAULT_GUEST_PERMISSIONS =
139 | List.of(Relationship.Permissions.USE_BLOCKS, Relationship.Permissions.USE_ENTITIES);
140 | }
141 |
142 | public static class Deserializer implements JsonDeserializer {
143 | final Class clazz;
144 |
145 | public Deserializer(Class clazz) {
146 | this.clazz = clazz;
147 | }
148 |
149 | @Override
150 | public T deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
151 | throws JsonParseException {
152 | if (!json.isJsonObject() && !json.getAsBoolean()) {
153 | return null;
154 | }
155 |
156 | return new Gson().fromJson(json, clazz);
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/api/events/FactionEvents.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.api.events;
2 |
3 | import io.icker.factions.api.persistents.Faction;
4 | import io.icker.factions.api.persistents.Home;
5 | import io.icker.factions.api.persistents.User;
6 |
7 | import net.fabricmc.fabric.api.event.Event;
8 | import net.fabricmc.fabric.api.event.EventFactory;
9 |
10 | /** Events related to {@link Faction} */
11 | public final class FactionEvents {
12 | /** Called when a {@link Faction} is created */
13 | public static final Event CREATE =
14 | EventFactory.createArrayBacked(
15 | Create.class,
16 | callbacks ->
17 | (faction, user) -> {
18 | for (Create callback : callbacks) {
19 | callback.onCreate(faction, user);
20 | }
21 | });
22 |
23 | /** Called when a {@link Faction} is disbanded */
24 | public static final Event DISBAND =
25 | EventFactory.createArrayBacked(
26 | Disband.class,
27 | callbacks ->
28 | (faction) -> {
29 | for (Disband callback : callbacks) {
30 | callback.onDisband(faction);
31 | }
32 | });
33 |
34 | /** Called when a {@link User} joins a {@link Faction} */
35 | public static final Event MEMBER_JOIN =
36 | EventFactory.createArrayBacked(
37 | MemberJoin.class,
38 | callbacks ->
39 | (faction, user) -> {
40 | for (MemberJoin callback : callbacks) {
41 | callback.onMemberJoin(faction, user);
42 | }
43 | });
44 |
45 | /** Called when a {@link User} leaves a {@link Faction} */
46 | public static final Event MEMBER_LEAVE =
47 | EventFactory.createArrayBacked(
48 | MemberLeave.class,
49 | callbacks ->
50 | (faction, user) -> {
51 | for (MemberLeave callback : callbacks) {
52 | callback.onMemberLeave(faction, user);
53 | }
54 | });
55 |
56 | /** Called when a factions name, description, MOTD, color or open status is modified */
57 | public static final Event MODIFY =
58 | EventFactory.createArrayBacked(
59 | Modify.class,
60 | callbacks ->
61 | (faction) -> {
62 | for (Modify callback : callbacks) {
63 | callback.onModify(faction);
64 | }
65 | });
66 |
67 | /** Called when a factions power changes */
68 | public static final Event POWER_CHANGE =
69 | EventFactory.createArrayBacked(
70 | PowerChange.class,
71 | callbacks ->
72 | (faction, oldPower) -> {
73 | for (PowerChange callback : callbacks) {
74 | callback.onPowerChange(faction, oldPower);
75 | }
76 | });
77 |
78 | /** Called when a faction sets its {@link Home} */
79 | public static final Event SET_HOME =
80 | EventFactory.createArrayBacked(
81 | SetHome.class,
82 | callbacks ->
83 | (faction, home) -> {
84 | for (SetHome callback : callbacks) {
85 | callback.onSetHome(faction, home);
86 | }
87 | });
88 |
89 | /**
90 | * Called when a faction removes all its claims. (Note that each claim will also run a {@link
91 | * ClaimEvents} REMOVE event)
92 | */
93 | public static final Event REMOVE_ALL_CLAIMS =
94 | EventFactory.createArrayBacked(
95 | RemoveAllClaims.class,
96 | callbacks ->
97 | (faction) -> {
98 | for (RemoveAllClaims callback : callbacks) {
99 | callback.onRemoveAllClaims(faction);
100 | }
101 | });
102 |
103 | @FunctionalInterface
104 | public interface Create {
105 | void onCreate(Faction faction, User owner);
106 | }
107 |
108 | @FunctionalInterface
109 | public interface Disband {
110 | void onDisband(Faction faction);
111 | }
112 |
113 | @FunctionalInterface
114 | public interface MemberJoin {
115 | void onMemberJoin(Faction faction, User user);
116 | }
117 |
118 | // TODO add Reason: LEAVE, KICK, DISBAND
119 | @FunctionalInterface
120 | public interface MemberLeave {
121 | void onMemberLeave(Faction faction, User user);
122 | }
123 |
124 | @FunctionalInterface
125 | public interface Modify {
126 | void onModify(Faction faction);
127 | }
128 |
129 | @FunctionalInterface
130 | public interface PowerChange {
131 | void onPowerChange(Faction faction, int oldPower);
132 | }
133 |
134 | @FunctionalInterface
135 | public interface SetHome {
136 | void onSetHome(Faction faction, Home home);
137 | }
138 |
139 | @FunctionalInterface
140 | public interface RemoveAllClaims {
141 | void onRemoveAllClaims(Faction faction);
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/geysermc-example-custom-skulls.yml:
--------------------------------------------------------------------------------
1 | player-profiles:
2 | - ewogICJ0aW1lc3RhbXAiIDogMTczMzcwNjkxNTU1MCwKICAicHJvZmlsZUlkIiA6ICJiZDgwZjkzZDBiODk0MjIwODVhMzZkNDFmMzE4ZmM5MiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZXl3b29kXzA2IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzVlYzUwM2I3NzQyZTMzM2FmMDc5MjRlMzA1M2QxMWM1MDllYjkxZDAxOWExMTFiNjVhM2UyNjJhMzc0Yzk3MDciLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ==
3 | - ewogICJ0aW1lc3RhbXAiIDogMTczMzgzMTA2MDY2MSwKICAicHJvZmlsZUlkIiA6ICI0OWIzODUyNDdhMWY0NTM3YjBmN2MwZTFmMTVjMTc2NCIsCiAgInByb2ZpbGVOYW1lIiA6ICJiY2QyMDMzYzYzZWM0YmY4IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzQyMzU0YTYwMTQ3YjdjY2Y1OWJhOTQ4OGZlNDFmNjM2YjYzNDFmOTJkZmY5YjM0MjIxNjA0MzNhN2VjNzI1N2QiLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ==
4 | - ewogICJ0aW1lc3RhbXAiIDogMTczMzcwNzAxMzY2MywKICAicHJvZmlsZUlkIiA6ICIzZGE2ZDgxOTI5MTY0MTNlODhlNzg2MjQ3NzA4YjkzZSIsCiAgInByb2ZpbGVOYW1lIiA6ICJGZXJTdGlsZSIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS81MjVkNDUzMGQwYjA5ODc2MzE0ZTE1NWYzMTViYTAyOWVmNDc4YTFhMzE2YzUyMWY1NzQzMWY1MmVlZTMxZmJiIiwKICAgICAgIm1ldGFkYXRhIiA6IHsKICAgICAgICAibW9kZWwiIDogInNsaW0iCiAgICAgIH0KICAgIH0KICB9Cn0=
5 | - eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTM3ZTMxNjY0NzhhY2Q3MDZhYjhiYTJkYjc4YzAzMzg2MTA4YTMwZWNjNjc4ZWMyNjRhMjdkZWZhZjAzNDA1OSJ9fX0=
6 | - eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjNjNzk1Y2U5ZWViNmRhNDMzMTU3MTZkNmNjYjg1Yjc4YWFmZTY2ZWRhMWJiY2Y5NjliMjAyODRiZTE5YTY1In19fQ==
7 | - eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjgyMDllZWEwYjYyYjBkMWY1NmQ4YWZkNGY5Y2UyOGYzNDMzNDkwY2I2NmMzNWYxYTA5ZWI3MWNhNzY2ZmZkYiJ9fX0=
8 | - eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2ZhNTE0OGU4MjFjMWZiZTIzZDYzZDA4Nzc2YjA1NWIwNjU0MDI4ZTJlMzk2NWVjMGRjNDhiNGU2MmNlNmQwNSJ9fX0=
9 | - ewogICJ0aW1lc3RhbXAiIDogMTczMzc0NDc3OTc5MywKICAicHJvZmlsZUlkIiA6ICJjNmViMzdjNmE4YjM0MDI3OGJjN2FmZGE3ZjMxOWJmMyIsCiAgInByb2ZpbGVOYW1lIiA6ICJFbFJleUNhbGFiYXphbCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS8zMTc0MGFkYWJmYzFiOWVlZGI3N2MyNDQ0NWRlNDE1ZmRmZmMzNDFhMTEwODM5NGIxZDlkZmQ5YjMzMjRiNzY1IiwKICAgICAgIm1ldGFkYXRhIiA6IHsKICAgICAgICAibW9kZWwiIDogInNsaW0iCiAgICAgIH0KICAgIH0KICB9Cn0=
10 | - eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWFlYTNjYWM2MDk5YjY5ZDJjZjdjNWE2MWU1YTk1NDI5MTdmNDU4MzFmN2Y2OWRkZmFmMzJlNjc2ZGJiODhkYyJ9fX0=
11 | - eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDE2Y2VlMTdhYTg1ZTAwNTgwMzIxNzU5YzE2ZGU4Y2Q0Nzc5MGY0Njk2MWFmY2ZiOGZiNWYzZWYyZjY2N2Y1NSJ9fX0=
12 | - eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzdhOGJmZmYwMjY5YzI1YTVhYzQ1NDI4YzQ2ZWZjZTkyODYwODI0YzE0ZDNjNTRjMjg0MmU1ZGUxODJjIn19fQ==
13 | - eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWM4MGY4NjdkOWM4MGY2OWUxYjBhMzZhYmQ1ZGM2ZDYzNmJmYWQyYjYyZGYzMmUwYTA4MWJmZTkxMWQ3NTVkMiJ9fX0=
14 | - ewogICJ0aW1lc3RhbXAiIDogMTczNDIzMjI0OTU3OSwKICAicHJvZmlsZUlkIiA6ICI1ZjU5NmViY2JlOTQ0NmQxYmI0M2JlNGYzZjRiOGJlNSIsCiAgInByb2ZpbGVOYW1lIiA6ICJUZWlsMHNzIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2FlNzgzZmMwNmRlOTRiYTU4YzYyMTc5MGNmMjMxYmZjNThhNGZhMGM1YjIwODdjN2IwOTY1NGI1YWM5YTc5YTIiLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ==
15 | - eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzEwODI5OGZmMmIyNjk1MWQ2ODNlNWFkZTQ2YTQyZTkwYzJmN2M3ZGQ0MWJhYTkwOGJjNTg1MmY4YzMyZTU4MyJ9fX0
16 | - ewogICJ0aW1lc3RhbXAiIDogMTY0MDYxNjE5MjE0MiwKICAicHJvZmlsZUlkIiA6ICJmMjc0YzRkNjI1MDQ0ZTQxOGVmYmYwNmM3NWIyMDIxMyIsCiAgInByb2ZpbGVOYW1lIiA6ICJIeXBpZ3NlbCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS81MDgyMGY3NmUzZTA0MWM3NWY3NmQwZjMwMTIzMmJkZjQ4MzIxYjUzNGZlNmE4NTljY2I4NzNkMjk4MWE5NjIzIiwKICAgICAgIm1ldGFkYXRhIiA6IHsKICAgICAgICAibW9kZWwiIDogInNsaW0iCiAgICAgIH0KICAgIH0KICB9Cn0=
17 | - eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzg2MTg1YjFkNTE5YWRlNTg1ZjE4NGMzNGYzZjNlMjBiYjY0MWRlYjg3OWU4MTM3OGU0ZWFmMjA5Mjg3In19fQ
18 | - ewogICJ0aW1lc3RhbXAiIDogMTY0MDYxNjExMDQ4OCwKICAicHJvZmlsZUlkIiA6ICIxZjEyNTNhYTVkYTQ0ZjU5YWU1YWI1NmFhZjRlNTYxNyIsCiAgInByb2ZpbGVOYW1lIiA6ICJOb3RNaUt5IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdlNTc3MjBhNDg3OGM4YmNhYjBlOWM5YzQ3ZDllNTUxMjhjY2Q3N2JhMzQ0NWE1NGE5MWUzZTFlMWEyNzM1NmUiLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ==
19 | - ewogICJ0aW1lc3RhbXAiIDogMTczMzc5NDEzMzQ5OSwKICAicHJvZmlsZUlkIiA6ICI1ZjU5NmViY2JlOTQ0NmQxYmI0M2JlNGYzZjRiOGJlNSIsCiAgInByb2ZpbGVOYW1lIiA6ICJUZWlsMHNzIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzhkNmQ1ZWM3NzlhM2JkMThlZTllODM3MTJkZDU0MjE5YjQ5NjM3ZjkwZWVlMmJkMThiMjNkYTZkOTlmYjcyMWIiCiAgICB9CiAgfQp9
20 | - ewogICJ0aW1lc3RhbXAiIDogMTczMzc5NDEzNTI5OSwKICAicHJvZmlsZUlkIiA6ICJlOThhZTBlMTI5MDg0ZDA5OTk0MTg4N2Q2YTk0ZTI2NCIsCiAgInByb2ZpbGVOYW1lIiA6ICJUYXVuYWhpWmVhbG90Qm90IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzMxNjMyZDIwZTQ1YjNlNmE2YWFlZmQzMjgwZmRlMmIzNjdiYmU4NTk3NGUwMzk1YTViZmY1YWNkZWU0MWU2NzYiCiAgICB9CiAgfQp9
21 | - ewogICJ0aW1lc3RhbXAiIDogMTczMzczNDc2MjMzMCwKICAicHJvZmlsZUlkIiA6ICIwMzBlMDA1OWQwY2M0YTZhODY3N2RkZWU3MjEzMjg1MyIsCiAgInByb2ZpbGVOYW1lIiA6ICJTbXVnRm9vZGllIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2U4NTJhZjkyMjIxZDZlMDI5NDcxYjA5MWMxNTI5YmZkOWJkZTI3MWU4MTk3NWQxY2ZiNmNjNDkzMDg3MDQzNTYiCiAgICB9CiAgfQp9
22 | - ewogICJ0aW1lc3RhbXAiIDogMTczMzczNDc2MDU1MiwKICAicHJvZmlsZUlkIiA6ICI5N2VmNDYyMzdhNGY0ZTQxYWY2ZTljYjg2MTdmNzc2OSIsCiAgInByb2ZpbGVOYW1lIiA6ICJZdWthcmlLYXplIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzczYThlY2QyYTRhMTMyNWVkNzM0MGJhNjgzMDdmZWZjOGI5MjQ0OWMzYTBiMzQxMzgyOTI4ZDIxODgzYjE4ODYiCiAgICB9CiAgfQp9
23 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/core/FactionsManager.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.core;
2 |
3 | import io.icker.factions.FactionsMod;
4 | import io.icker.factions.api.events.ClaimEvents;
5 | import io.icker.factions.api.events.FactionEvents;
6 | import io.icker.factions.api.events.PlayerEvents;
7 | import io.icker.factions.api.persistents.Faction;
8 | import io.icker.factions.api.persistents.Home;
9 | import io.icker.factions.api.persistents.User;
10 | import io.icker.factions.util.Message;
11 | import io.icker.factions.util.WorldUtils;
12 |
13 | import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
14 | import net.minecraft.entity.damage.DamageSource;
15 | import net.minecraft.entity.player.PlayerEntity;
16 | import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket;
17 | import net.minecraft.screen.GenericContainerScreenHandler;
18 | import net.minecraft.screen.SimpleNamedScreenHandlerFactory;
19 | import net.minecraft.server.MinecraftServer;
20 | import net.minecraft.server.PlayerManager;
21 | import net.minecraft.server.network.ServerPlayerEntity;
22 | import net.minecraft.server.world.ServerWorld;
23 | import net.minecraft.text.Text;
24 | import net.minecraft.util.ActionResult;
25 | import net.minecraft.util.math.BlockPos;
26 | import net.minecraft.util.math.ChunkPos;
27 |
28 | import java.util.EnumSet;
29 | import java.util.List;
30 | import java.util.Objects;
31 |
32 | public class FactionsManager {
33 | public static PlayerManager playerManager;
34 |
35 | public static void register() {
36 | ServerLifecycleEvents.SERVER_STARTED.register(FactionsManager::serverStarted);
37 | FactionEvents.MODIFY.register(FactionsManager::factionModified);
38 | FactionEvents.MEMBER_JOIN.register(FactionsManager::memberChange);
39 | FactionEvents.MEMBER_LEAVE.register(FactionsManager::memberChange);
40 | PlayerEvents.ON_KILLED_BY_PLAYER.register(FactionsManager::playerDeath);
41 | PlayerEvents.ON_POWER_TICK.register(FactionsManager::powerTick);
42 | PlayerEvents.OPEN_SAFE.register(FactionsManager::openSafe);
43 |
44 | if (FactionsMod.CONFIG.HOME != null && FactionsMod.CONFIG.HOME.CLAIM_ONLY) {
45 | ClaimEvents.REMOVE.register(
46 | (x, z, level, faction) -> {
47 | Home home = faction.getHome();
48 |
49 | if (home == null || !Objects.equals(home.level, level)) {
50 | return;
51 | }
52 |
53 | BlockPos homePos = BlockPos.ofFloored(home.x, home.y, home.z);
54 |
55 | ServerWorld world = WorldUtils.getWorld(home.level);
56 |
57 | ChunkPos homeChunkPos = world.getChunk(homePos).getPos();
58 |
59 | if (homeChunkPos.x == x && homeChunkPos.z == z) {
60 | faction.setHome(null);
61 | }
62 | });
63 | }
64 | }
65 |
66 | private static void serverStarted(MinecraftServer server) {
67 | playerManager = server.getPlayerManager();
68 | Message.manager = server.getPlayerManager();
69 | }
70 |
71 | private static void factionModified(Faction faction) {
72 | ServerPlayerEntity[] players =
73 | faction.getUsers().stream()
74 | .map(user -> playerManager.getPlayer(user.getID()))
75 | .filter(player -> player != null)
76 | .toArray(ServerPlayerEntity[]::new);
77 | updatePlayerList(players);
78 | }
79 |
80 | private static void memberChange(Faction faction, User user) {
81 | ServerPlayerEntity player = playerManager.getPlayer(user.getID());
82 | if (player != null) {
83 | updatePlayerList(player);
84 | }
85 | }
86 |
87 | private static void playerDeath(ServerPlayerEntity player, DamageSource source) {
88 | User member = User.get(player.getUuid());
89 | if (!member.isInFaction()) return;
90 |
91 | Faction faction = member.getFaction();
92 |
93 | int adjusted = faction.adjustPower(-FactionsMod.CONFIG.POWER.DEATH_PENALTY);
94 | new Message(
95 | Text.translatable(
96 | "factions.events.lose_power_by_death",
97 | player.getName().getString(),
98 | adjusted))
99 | .send(faction);
100 | }
101 |
102 | private static void powerTick(ServerPlayerEntity player) {
103 | User member = User.get(player.getUuid());
104 | if (!member.isInFaction()) return;
105 |
106 | Faction faction = member.getFaction();
107 |
108 | int adjusted = faction.adjustPower(FactionsMod.CONFIG.POWER.POWER_TICKS.REWARD);
109 | if (adjusted != 0 && FactionsMod.CONFIG.DISPLAY.POWER_MESSAGE)
110 | new Message(
111 | Text.translatable(
112 | "factions.events.get_power_by_tick",
113 | player.getName().getString(),
114 | adjusted))
115 | .send(faction);
116 | }
117 |
118 | private static void updatePlayerList(ServerPlayerEntity... players) {
119 | playerManager.sendToAll(
120 | new PlayerListS2CPacket(
121 | EnumSet.of(PlayerListS2CPacket.Action.UPDATE_DISPLAY_NAME),
122 | List.of(players)));
123 | }
124 |
125 | private static ActionResult openSafe(PlayerEntity player, Faction faction) {
126 | User user = User.get(player.getUuid());
127 |
128 | if (!user.isInFaction()) {
129 | if (FactionsMod.CONFIG.SAFE != null && FactionsMod.CONFIG.SAFE.ENDER_CHEST) {
130 | new Message(Text.translatable("factions.events.no_enderchests_without_faction"))
131 | .fail()
132 | .send(player, false);
133 | return ActionResult.FAIL;
134 | }
135 | return ActionResult.PASS;
136 | }
137 |
138 | player.openHandledScreen(
139 | new SimpleNamedScreenHandlerFactory(
140 | (syncId, inventory, p) -> {
141 | if (FactionsMod.CONFIG.SAFE.DOUBLE) {
142 | return GenericContainerScreenHandler.createGeneric9x6(
143 | syncId, inventory, faction.getSafe());
144 | } else {
145 | return GenericContainerScreenHandler.createGeneric9x3(
146 | syncId, inventory, faction.getSafe());
147 | }
148 | },
149 | Text.translatable("factions.gui.safe.title", faction.getName())));
150 |
151 | return ActionResult.SUCCESS;
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/util/PlaceholdersWrapper.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.util;
2 |
3 | import eu.pb4.placeholders.api.PlaceholderResult;
4 | import eu.pb4.placeholders.api.Placeholders;
5 |
6 | import io.icker.factions.FactionsMod;
7 | import io.icker.factions.api.persistents.Faction;
8 | import io.icker.factions.api.persistents.User;
9 |
10 | import net.minecraft.text.Style;
11 | import net.minecraft.text.Text;
12 | import net.minecraft.util.Formatting;
13 | import net.minecraft.util.Identifier;
14 |
15 | import xyz.nucleoid.server.translations.api.Localization;
16 | import xyz.nucleoid.server.translations.api.language.ServerLanguage;
17 |
18 | import java.util.function.Function;
19 |
20 | public class PlaceholdersWrapper {
21 | private static final Text UNFORMATTED_NULL = Text.translatable("factions.papi.factionless");
22 | private static final Text FORMATTED_NULL =
23 | UNFORMATTED_NULL.copy().formatted(Formatting.DARK_GRAY);
24 |
25 | private static void register(String identifier, Function handler) {
26 | Placeholders.register(
27 | Identifier.of(FactionsMod.MODID, identifier),
28 | (ctx, argument) -> {
29 | if (!ctx.hasPlayer())
30 | return PlaceholderResult.invalid(
31 | Localization.raw(
32 | "argument.entity.notfound.player",
33 | ServerLanguage.getLanguage(FactionsMod.CONFIG.LANGUAGE)));
34 |
35 | User member = User.get(ctx.player().getUuid());
36 | return PlaceholderResult.value(handler.apply(member));
37 | });
38 | }
39 |
40 | public static void init() {
41 | register(
42 | "name",
43 | (member) -> {
44 | Faction faction = member.getFaction();
45 | if (faction == null) return FORMATTED_NULL;
46 |
47 | return Text.literal(faction.getName())
48 | .formatted(member.getFaction().getColor());
49 | });
50 |
51 | register(
52 | "colorless_name",
53 | (member) -> {
54 | Faction faction = member.getFaction();
55 | if (faction == null) return FORMATTED_NULL;
56 |
57 | return Text.of(faction.getName());
58 | });
59 |
60 | register(
61 | "chat",
62 | (member) -> {
63 | if (member.chat == User.ChatMode.GLOBAL || !member.isInFaction())
64 | return Text.translatable("factions.papi.chat.global");
65 |
66 | return Text.translatable("factions.papi.chat.faction");
67 | });
68 |
69 | register(
70 | "rank",
71 | (member) -> {
72 | if (!member.isInFaction()) return FORMATTED_NULL;
73 |
74 | return Text.of(member.getRankName());
75 | });
76 |
77 | register(
78 | "color",
79 | (member) -> {
80 | if (!member.isInFaction()) return Text.of("reset");
81 |
82 | return Text.of(member.getFaction().getColor().getName());
83 | });
84 |
85 | register(
86 | "description",
87 | (member) -> {
88 | Faction faction = member.getFaction();
89 | if (faction == null) return FORMATTED_NULL;
90 |
91 | return Text.of(faction.getDescription());
92 | });
93 |
94 | register(
95 | "state",
96 | (member) -> {
97 | Faction faction = member.getFaction();
98 | if (faction == null) return UNFORMATTED_NULL;
99 |
100 | return Text.of(String.valueOf(faction.isOpen()));
101 | });
102 |
103 | register(
104 | "power",
105 | (member) -> {
106 | Faction faction = member.getFaction();
107 | if (faction == null) return UNFORMATTED_NULL;
108 |
109 | return Text.of(String.valueOf(faction.getPower()));
110 | });
111 |
112 | register(
113 | "power_formatted",
114 | (member) -> {
115 | Faction faction = member.getFaction();
116 | if (faction == null) return FORMATTED_NULL;
117 |
118 | int red =
119 | mapBoundRange(
120 | faction.calculateMaxPower(), 0, 170, 255, faction.getPower());
121 | int green =
122 | mapBoundRange(
123 | 0, faction.calculateMaxPower(), 170, 255, faction.getPower());
124 | return Text.literal(String.valueOf(faction.getPower()))
125 | .setStyle(Style.EMPTY.withColor(rgbToInt(red, green, 170)));
126 | });
127 |
128 | register(
129 | "max_power",
130 | (member) -> {
131 | Faction faction = member.getFaction();
132 | if (faction == null) return UNFORMATTED_NULL;
133 |
134 | return Text.of(String.valueOf(faction.calculateMaxPower()));
135 | });
136 |
137 | register(
138 | "player_power",
139 | (member) -> {
140 | return Text.of(String.valueOf(FactionsMod.CONFIG.POWER.MEMBER));
141 | });
142 |
143 | register(
144 | "required_power",
145 | (member) -> {
146 | Faction faction = member.getFaction();
147 | if (faction == null) return UNFORMATTED_NULL;
148 |
149 | return Text.of(
150 | String.valueOf(
151 | faction.getClaims().size()
152 | * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT));
153 | });
154 |
155 | register(
156 | "required_power_formatted",
157 | (member) -> {
158 | Faction faction = member.getFaction();
159 | if (faction == null) return FORMATTED_NULL;
160 |
161 | int reqPower =
162 | faction.getClaims().size() * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT;
163 | int red = mapBoundRange(0, faction.getPower(), 85, 255, reqPower);
164 | return Text.literal(String.valueOf(reqPower))
165 | .setStyle(Style.EMPTY.withColor(rgbToInt(red, 85, 85)));
166 | });
167 | }
168 |
169 | private static int rgbToInt(int red, int green, int blue) {
170 | return (red & 255 << 16) | (green & 255 << 8) | (blue & 255);
171 | }
172 |
173 | private static int mapBoundRange(
174 | int from_min, int from_max, int to_min, int to_max, int value) {
175 | return Math.min(
176 | to_max,
177 | Math.max(
178 | to_min,
179 | to_min + ((value - from_min) * (to_max - to_min)) / (from_max - from_min)));
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/command/HomeCommand.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.command;
2 |
3 | import com.mojang.brigadier.context.CommandContext;
4 | import com.mojang.brigadier.exceptions.CommandSyntaxException;
5 | import com.mojang.brigadier.tree.LiteralCommandNode;
6 |
7 | import io.icker.factions.FactionsMod;
8 | import io.icker.factions.api.persistents.Claim;
9 | import io.icker.factions.api.persistents.Faction;
10 | import io.icker.factions.api.persistents.Home;
11 | import io.icker.factions.api.persistents.User;
12 | import io.icker.factions.mixin.DamageTrackerAccessor;
13 | import io.icker.factions.util.Command;
14 | import io.icker.factions.util.Message;
15 | import io.icker.factions.util.WorldUtils;
16 |
17 | import net.minecraft.server.command.CommandManager;
18 | import net.minecraft.server.command.ServerCommandSource;
19 | import net.minecraft.server.network.ServerPlayerEntity;
20 | import net.minecraft.server.world.ServerWorld;
21 | import net.minecraft.text.Text;
22 | import net.minecraft.util.math.BlockPos;
23 | import net.minecraft.util.math.ChunkPos;
24 |
25 | import java.time.Instant;
26 | import java.util.Date;
27 | import java.util.HashSet;
28 |
29 | public class HomeCommand implements Command {
30 | private int go(CommandContext context) throws CommandSyntaxException {
31 | ServerCommandSource source = context.getSource();
32 | ServerPlayerEntity player = source.getPlayerOrThrow();
33 |
34 | if (player == null) return 0;
35 |
36 | User user = Command.getUser(player);
37 | Faction faction = user.getFaction();
38 |
39 | if (faction == null) return 0;
40 |
41 | return execGo(player, user, faction);
42 | }
43 |
44 | public int execGo(ServerPlayerEntity player, User user, Faction faction) {
45 | Home home = faction.getHome();
46 |
47 | if (home == null) {
48 | new Message(Text.translatable("factions.command.home.warp.fail.no_home"))
49 | .fail()
50 | .send(player, false);
51 | return 0;
52 | }
53 |
54 | if (player.getEntityWorld().getServer() == null) return 0;
55 |
56 | ServerWorld world = WorldUtils.getWorld(home.level);
57 |
58 | if (world == null) {
59 | new Message(Text.translatable("factions.command.home.warp.fail.no_world"))
60 | .fail()
61 | .send(player, false);
62 | return 0;
63 | }
64 |
65 | if (checkLimitToClaim(faction, world, BlockPos.ofFloored(home.x, home.y, home.z))) {
66 | new Message(Text.translatable("factions.command.home.warp.fail.no_claim"))
67 | .fail()
68 | .send(player, false);
69 | return 0;
70 | }
71 |
72 | long elapsed_time = Date.from(Instant.now()).getTime() - user.homeCooldown;
73 | if (elapsed_time < FactionsMod.CONFIG.HOME.HOME_WARP_COOLDOWN_SECOND * 1000) {
74 | new Message(
75 | "Cannot warp home while on warp cooldown, please wait %.0f seconds",
76 | (double)
77 | (FactionsMod.CONFIG.HOME.HOME_WARP_COOLDOWN_SECOND
78 | * 1000
79 | - elapsed_time)
80 | / 1000.0)
81 | .fail()
82 | .send(player, false);
83 | return 0;
84 | }
85 |
86 | if (((DamageTrackerAccessor) player.getDamageTracker()).getAgeOnLastDamage() == 0
87 | || player.age
88 | - ((DamageTrackerAccessor) player.getDamageTracker())
89 | .getAgeOnLastDamage()
90 | > FactionsMod.CONFIG.HOME.DAMAGE_COOLDOWN) {
91 | player.teleport(
92 | world, home.x, home.y, home.z, new HashSet<>(), home.yaw, home.pitch, false);
93 | user.homeCooldown = Date.from(Instant.now()).getTime();
94 |
95 | new Message(Text.translatable("factions.command.home.warp.success"))
96 | .send(player, false);
97 | } else {
98 | new Message(Text.translatable("factions.command.home.warp.fail.combat"))
99 | .fail()
100 | .send(player, false);
101 | }
102 | return 1;
103 | }
104 |
105 | private int set(CommandContext context) throws CommandSyntaxException {
106 | ServerCommandSource source = context.getSource();
107 | ServerPlayerEntity player = source.getPlayerOrThrow();
108 |
109 | Faction faction = Command.getUser(player).getFaction();
110 |
111 | if (checkLimitToClaim(
112 | faction, (ServerWorld) player.getEntityWorld(), player.getBlockPos())) {
113 | new Message(Text.translatable("factions.command.home.fail.no_claim"))
114 | .fail()
115 | .send(player, false);
116 | return 0;
117 | }
118 |
119 | Home home =
120 | new Home(
121 | faction.getID(),
122 | player.getX(),
123 | player.getY(),
124 | player.getZ(),
125 | player.getHeadYaw(),
126 | player.getPitch(),
127 | player.getEntityWorld().getRegistryKey().getValue().toString());
128 |
129 | faction.setHome(home);
130 | new Message(
131 | Text.translatable(
132 | "factions.command.home.set.success",
133 | home.x,
134 | home.y,
135 | home.z,
136 | player.getName().getString()))
137 | .send(faction);
138 | return 1;
139 | }
140 |
141 | private static boolean checkLimitToClaim(Faction faction, ServerWorld world, BlockPos pos) {
142 | if (!FactionsMod.CONFIG.HOME.CLAIM_ONLY) return false;
143 |
144 | ChunkPos chunkPos = world.getChunk(pos).getPos();
145 | String dimension = world.getRegistryKey().getValue().toString();
146 |
147 | Claim possibleClaim = Claim.get(chunkPos.x, chunkPos.z, dimension);
148 | return possibleClaim == null || possibleClaim.getFaction().getID() != faction.getID();
149 | }
150 |
151 | @Override
152 | public LiteralCommandNode getNode() {
153 | return CommandManager.literal("home")
154 | .requires(
155 | Requires.multiple(
156 | Requires.isMember(),
157 | s -> FactionsMod.CONFIG.HOME != null,
158 | Requires.hasPerms("factions.home", 0)))
159 | .executes(this::go)
160 | .then(
161 | CommandManager.literal("set")
162 | .requires(
163 | Requires.multiple(
164 | Requires.hasPerms("factions.home.set", 0),
165 | Requires.isLeader()))
166 | .executes(this::set))
167 | .build();
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/command/InviteCommand.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.command;
2 |
3 | import com.mojang.authlib.GameProfile;
4 | import com.mojang.brigadier.context.CommandContext;
5 | import com.mojang.brigadier.exceptions.CommandSyntaxException;
6 | import com.mojang.brigadier.tree.LiteralCommandNode;
7 |
8 | import io.icker.factions.api.persistents.Faction;
9 | import io.icker.factions.api.persistents.User;
10 | import io.icker.factions.util.Command;
11 | import io.icker.factions.util.Message;
12 |
13 | import net.minecraft.command.argument.EntityArgumentType;
14 | import net.minecraft.server.GameProfileResolver;
15 | import net.minecraft.server.command.CommandManager;
16 | import net.minecraft.server.command.ServerCommandSource;
17 | import net.minecraft.server.network.ServerPlayerEntity;
18 | import net.minecraft.text.Text;
19 | import net.minecraft.util.Formatting;
20 | import net.minecraft.util.Util;
21 |
22 | import xyz.nucleoid.server.translations.api.Localization;
23 |
24 | import java.util.List;
25 | import java.util.UUID;
26 | import java.util.stream.Collectors;
27 |
28 | public class InviteCommand implements Command {
29 | private int list(CommandContext context) throws CommandSyntaxException {
30 | ServerCommandSource source = context.getSource();
31 |
32 | ServerPlayerEntity player = source.getPlayerOrThrow();
33 | List invites = Command.getUser(player).getFaction().invites;
34 | int count = invites.size();
35 |
36 | new Message(
37 | Text.translatable(
38 | "factions.command.invite.list",
39 | Text.literal(String.valueOf(count)).formatted(Formatting.YELLOW)))
40 | .send(player, false);
41 |
42 | if (count == 0) return 1;
43 |
44 | GameProfileResolver resolver = source.getServer().getApiServices().profileResolver();
45 | String players =
46 | invites.stream()
47 | .map(
48 | invite ->
49 | resolver.getProfileById(invite)
50 | .orElse(
51 | new GameProfile(
52 | Util.NIL_UUID,
53 | Localization.raw(
54 | "factions.gui.generic.unknown_player",
55 | player)))
56 | .name())
57 | .collect(Collectors.joining(", "));
58 |
59 | new Message(players).format(Formatting.ITALIC).send(player, false);
60 | return 1;
61 | }
62 |
63 | private int add(CommandContext context) throws CommandSyntaxException {
64 | ServerPlayerEntity target = EntityArgumentType.getPlayer(context, "player");
65 |
66 | ServerCommandSource source = context.getSource();
67 | ServerPlayerEntity player = source.getPlayerOrThrow();
68 |
69 | Faction faction = Command.getUser(source.getPlayerOrThrow()).getFaction();
70 | if (faction.isInvited(player.getUuid())) {
71 | new Message(
72 | Text.translatable(
73 | "factions.command.invite.add.fail.already_invited",
74 | target.getName().getString()))
75 | .fail()
76 | .send(player, false);
77 | return 0;
78 | }
79 |
80 | User targetUser = User.get(target.getUuid());
81 | UUID targetFaction = targetUser.isInFaction() ? targetUser.getFaction().getID() : null;
82 | if (faction.getID().equals(targetFaction)) {
83 | new Message(
84 | Text.translatable(
85 | "factions.command.invite.add.fail.already_member",
86 | target.getName().getString()))
87 | .fail()
88 | .send(player, false);
89 | return 0;
90 | }
91 |
92 | faction.invites.add(target.getUuid());
93 |
94 | new Message(
95 | Text.translatable(
96 | "factions.command.invite.add.success.actor",
97 | target.getName().getString()))
98 | .send(faction);
99 | new Message(Text.translatable("factions.command.invite.add.success.subject"))
100 | .format(Formatting.YELLOW)
101 | .hover(Text.translatable("factions.command.invite.add.success.subject.hover"))
102 | .click("/factions join " + faction.getName())
103 | .prependFaction(faction)
104 | .send(target, false);
105 | return 1;
106 | }
107 |
108 | private int remove(CommandContext context) throws CommandSyntaxException {
109 | ServerPlayerEntity target = EntityArgumentType.getPlayer(context, "player");
110 |
111 | ServerCommandSource source = context.getSource();
112 | ServerPlayerEntity player = source.getPlayerOrThrow();
113 |
114 | Faction faction = Command.getUser(player).getFaction();
115 | if (faction.invites.remove(target.getUuid())) {
116 | new Message(
117 | Text.translatable(
118 | "factions.command.invite.remove.success",
119 | target.getName().getString()))
120 | .send(player, false);
121 | return 1;
122 | } else {
123 | new Message(
124 | Text.translatable(
125 | "factions.command.invite.remove.fail",
126 | target.getName().getString()))
127 | .fail()
128 | .send(player, false);
129 | return 0;
130 | }
131 | }
132 |
133 | public LiteralCommandNode getNode() {
134 | return CommandManager.literal("invite")
135 | .requires(Requires.isCommander())
136 | .then(
137 | CommandManager.literal("list")
138 | .requires(Requires.hasPerms("factions.invite.list", 0))
139 | .executes(this::list))
140 | .then(
141 | CommandManager.literal("add")
142 | .requires(Requires.hasPerms("factions.invite.add", 0))
143 | .then(
144 | CommandManager.argument(
145 | "player", EntityArgumentType.player())
146 | .executes(this::add)))
147 | .then(
148 | CommandManager.literal("remove")
149 | .requires(Requires.hasPerms("factions.invite.remove", 0))
150 | .then(
151 | CommandManager.argument(
152 | "player", EntityArgumentType.player())
153 | .executes(this::remove)))
154 | .build();
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/command/SettingsCommand.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.command;
2 |
3 | import com.mojang.brigadier.context.CommandContext;
4 | import com.mojang.brigadier.exceptions.CommandSyntaxException;
5 | import com.mojang.brigadier.tree.LiteralCommandNode;
6 |
7 | import io.icker.factions.api.persistents.User;
8 | import io.icker.factions.util.Command;
9 | import io.icker.factions.util.Message;
10 |
11 | import net.minecraft.server.command.CommandManager;
12 | import net.minecraft.server.command.ServerCommandSource;
13 | import net.minecraft.server.network.ServerPlayerEntity;
14 | import net.minecraft.text.Text;
15 | import net.minecraft.util.Formatting;
16 |
17 | public class SettingsCommand implements Command {
18 | private int setChat(CommandContext context, User.ChatMode option)
19 | throws CommandSyntaxException {
20 | ServerPlayerEntity player = context.getSource().getPlayerOrThrow();
21 | User user = User.get(player.getUuid());
22 | user.chat = option;
23 |
24 | new Message(Text.translatable("factions.command.settings.chat"))
25 | .filler("·")
26 | .add(
27 | new Message(
28 | Text.translatable(
29 | "factions.command.settings.chat."
30 | + user.getChatName()))
31 | .format(Formatting.BLUE))
32 | .send(player, false);
33 |
34 | return 1;
35 | }
36 |
37 | private int setSounds(CommandContext context, User.SoundMode option)
38 | throws CommandSyntaxException {
39 | ServerPlayerEntity player = context.getSource().getPlayerOrThrow();
40 | User user = User.get(player.getUuid());
41 | user.sounds = option;
42 |
43 | new Message(Text.translatable("factions.command.settings.sound"))
44 | .filler("·")
45 | .add(
46 | new Message(
47 | Text.translatable(
48 | "factions.command.settings.sound."
49 | + user.getSoundName()))
50 | .format(Formatting.BLUE))
51 | .send(player, false);
52 |
53 | return 1;
54 | }
55 |
56 | private int radar(CommandContext context) throws CommandSyntaxException {
57 | ServerCommandSource source = context.getSource();
58 | ServerPlayerEntity player = source.getPlayerOrThrow();
59 |
60 | User config = User.get(player.getUuid());
61 | boolean radar = !config.radar;
62 | config.radar = radar;
63 |
64 | new Message(Text.translatable("factions.command.settings.radar"))
65 | .filler("·")
66 | .add(
67 | new Message(Text.translatable("options." + (radar ? "on" : "off")))
68 | .format(radar ? Formatting.GREEN : Formatting.RED))
69 | .send(player, false);
70 |
71 | return 1;
72 | }
73 |
74 | public LiteralCommandNode getNode() {
75 | return CommandManager.literal("settings")
76 | .requires(Requires.hasPerms("factions.settings", 0))
77 | .then(
78 | CommandManager.literal("chat")
79 | .requires(Requires.hasPerms("factions.settings.chat", 0))
80 | .then(
81 | CommandManager.literal("global")
82 | .executes(
83 | context ->
84 | setChat(
85 | context,
86 | User.ChatMode.GLOBAL)))
87 | .then(
88 | CommandManager.literal("faction")
89 | .executes(
90 | context ->
91 | setChat(
92 | context,
93 | User.ChatMode.FACTION)))
94 | .then(
95 | CommandManager.literal("focus")
96 | .executes(
97 | context ->
98 | setChat(
99 | context,
100 | User.ChatMode.FOCUS))))
101 | .then(
102 | CommandManager.literal("radar")
103 | .requires(Requires.hasPerms("factions.settings.radar", 0))
104 | .executes(this::radar))
105 | .then(
106 | CommandManager.literal("sounds")
107 | .requires(Requires.hasPerms("factions.settings.sounds", 0))
108 | .then(
109 | CommandManager.literal("none")
110 | .executes(
111 | context ->
112 | setSounds(
113 | context,
114 | User.SoundMode.NONE)))
115 | .then(
116 | CommandManager.literal("warnings")
117 | .executes(
118 | context ->
119 | setSounds(
120 | context,
121 | User.SoundMode.WARNINGS)))
122 | .then(
123 | CommandManager.literal("faction")
124 | .executes(
125 | context ->
126 | setSounds(
127 | context,
128 | User.SoundMode.FACTION)))
129 | .then(
130 | CommandManager.literal("all")
131 | .executes(
132 | context ->
133 | setSounds(
134 | context,
135 | User.SoundMode.ALL))))
136 | .build();
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/database/Database.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.database;
2 |
3 | import io.icker.factions.FactionsMod;
4 |
5 | import net.fabricmc.loader.api.FabricLoader;
6 | import net.minecraft.nbt.NbtCompound;
7 | import net.minecraft.nbt.NbtElement;
8 | import net.minecraft.nbt.NbtIo;
9 | import net.minecraft.nbt.NbtList;
10 | import net.minecraft.nbt.NbtSizeTracker;
11 |
12 | import java.io.File;
13 | import java.io.IOException;
14 | import java.lang.reflect.Field;
15 | import java.lang.reflect.ParameterizedType;
16 | import java.nio.file.Path;
17 | import java.util.ArrayList;
18 | import java.util.HashMap;
19 | import java.util.List;
20 | import java.util.Map;
21 | import java.util.function.Function;
22 |
23 | public class Database {
24 | private static final File BASE_PATH =
25 | FabricLoader.getInstance().getGameDir().resolve("factions").toFile();
26 | private static final HashMap, HashMap> cache =
27 | new HashMap, HashMap>();
28 | private static final String KEY = "CORE";
29 |
30 | public static HashMap load(Class clazz, Function getStoreKey) {
31 | String name = clazz.getAnnotation(Name.class).value();
32 | File file = new File(BASE_PATH, name.toLowerCase() + ".dat");
33 |
34 | if (!cache.containsKey(clazz)) setup(clazz);
35 |
36 | HashMap store = new HashMap();
37 |
38 | if (!file.exists()) {
39 | if (!BASE_PATH.exists()) BASE_PATH.mkdir();
40 | try {
41 | file.createNewFile();
42 | } catch (IOException e) {
43 | FactionsMod.LOGGER.error("Failed to create file ({})", file, e);
44 | }
45 | return store;
46 | }
47 |
48 | try {
49 | NbtList list =
50 | (NbtList)
51 | NbtIo.readCompressed(
52 | Path.of(file.getPath()),
53 | NbtSizeTracker.ofUnlimitedBytes())
54 | .get(KEY);
55 | for (T item : deserializeList(clazz, list)) {
56 | store.put(getStoreKey.apply(item), item);
57 | }
58 | } catch (IOException | ReflectiveOperationException e) {
59 | FactionsMod.LOGGER.error("Failed to read NBT data ({})", file, e);
60 | }
61 |
62 | return store;
63 | }
64 |
65 | private static T deserialize(Class clazz, NbtElement value)
66 | throws IOException, ReflectiveOperationException {
67 | if (SerializerRegistry.contains(clazz)) {
68 | return SerializerRegistry.fromNbtElement(clazz, value);
69 | }
70 |
71 | NbtCompound compound = (NbtCompound) value;
72 | T item = (T) clazz.getDeclaredConstructor().newInstance();
73 |
74 | HashMap fields = cache.get(clazz);
75 | for (Map.Entry entry : fields.entrySet()) {
76 | String key = entry.getKey();
77 | Field field = entry.getValue();
78 |
79 | if (!compound.contains(key)) continue;
80 |
81 | Class> type = field.getType();
82 |
83 | if (ArrayList.class.isAssignableFrom(type)) {
84 | Class> genericType =
85 | (Class>)
86 | ((ParameterizedType) field.getGenericType())
87 | .getActualTypeArguments()[0];
88 | field.set(item, deserializeList(genericType, (NbtList) compound.get(key)));
89 | } else {
90 | field.set(item, deserialize(type, compound.get(key)));
91 | }
92 | }
93 |
94 | return item;
95 | }
96 |
97 | private static ArrayList deserializeList(Class clazz, NbtList list)
98 | throws IOException, ReflectiveOperationException {
99 | ArrayList store = new ArrayList();
100 |
101 | for (int i = 0; i < list.size(); i++) {
102 | store.add(deserialize(clazz, list.get(i)));
103 | }
104 |
105 | return store;
106 | }
107 |
108 | public static void save(Class clazz, List items) {
109 | String name = clazz.getAnnotation(Name.class).value();
110 | File file = new File(BASE_PATH, name.toLowerCase() + ".dat");
111 |
112 | if (!cache.containsKey(clazz)) setup(clazz);
113 |
114 | try {
115 | NbtCompound fileData = new NbtCompound();
116 | fileData.put(KEY, serializeList(clazz, items));
117 | NbtIo.writeCompressed(fileData, Path.of(file.getPath()));
118 | } catch (IOException | ReflectiveOperationException e) {
119 | FactionsMod.LOGGER.error("Failed to write NBT data ({})", file, e);
120 | }
121 | }
122 |
123 | private static NbtElement serialize(Class clazz, T item)
124 | throws IOException, ReflectiveOperationException {
125 | if (SerializerRegistry.contains(clazz)) {
126 | return SerializerRegistry.toNbtElement(clazz, item);
127 | }
128 |
129 | HashMap fields = cache.get(clazz);
130 | NbtCompound compound = new NbtCompound();
131 | for (Map.Entry entry : fields.entrySet()) {
132 | String key = entry.getKey();
133 | Field field = entry.getValue();
134 |
135 | Class> type = field.getType();
136 | Object data = field.get(item);
137 |
138 | if (data == null) continue;
139 |
140 | if (ArrayList.class.isAssignableFrom(type)) {
141 | Class> genericType =
142 | (Class>)
143 | ((ParameterizedType) field.getGenericType())
144 | .getActualTypeArguments()[0];
145 | compound.put(key, serializeList(genericType, cast(data)));
146 | } else {
147 | compound.put(key, serialize(type, cast(data)));
148 | }
149 | }
150 |
151 | return compound;
152 | }
153 |
154 | private static NbtList serializeList(Class clazz, List items)
155 | throws IOException, ReflectiveOperationException {
156 | NbtList list = new NbtList();
157 |
158 | for (T item : items) {
159 | list.add(list.size(), serialize(clazz, item));
160 | }
161 |
162 | return list;
163 | }
164 |
165 | private static void setup(Class clazz) {
166 | HashMap fields = new HashMap();
167 |
168 | for (Field field : clazz.getDeclaredFields()) {
169 | if (field.isAnnotationPresent(io.icker.factions.database.Field.class)) {
170 | field.setAccessible(true);
171 | fields.put(
172 | field.getAnnotation(io.icker.factions.database.Field.class).value(), field);
173 |
174 | Class> type = field.getType();
175 | if (!SerializerRegistry.contains(type)) {
176 | if (ArrayList.class.isAssignableFrom(type)) {
177 | ParameterizedType genericType = (ParameterizedType) field.getGenericType();
178 | setup((Class>) genericType.getActualTypeArguments()[0]);
179 | } else {
180 | setup(type);
181 | }
182 | }
183 | }
184 | }
185 |
186 | cache.put(clazz, fields);
187 | }
188 |
189 | @SuppressWarnings("unchecked")
190 | private static T cast(Object key) {
191 | return (T) key;
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/util/Icons.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.util;
2 |
3 | public class Icons {
4 | public static final String GUI_TESSERACT_BLUE =
5 | "ewogICJ0aW1lc3RhbXAiIDogMTczMzcwNjkxNTU1MCwKICAicHJvZmlsZUlkIiA6ICJiZDgwZjkzZDBiODk0MjIwODVhMzZkNDFmMzE4ZmM5MiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZXl3b29kXzA2IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzVlYzUwM2I3NzQyZTMzM2FmMDc5MjRlMzA1M2QxMWM1MDllYjkxZDAxOWExMTFiNjVhM2UyNjJhMzc0Yzk3MDciLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ==";
6 | public static final String GUI_TESSERACT_RED =
7 | "ewogICJ0aW1lc3RhbXAiIDogMTczMzgzMTA2MDY2MSwKICAicHJvZmlsZUlkIiA6ICI0OWIzODUyNDdhMWY0NTM3YjBmN2MwZTFmMTVjMTc2NCIsCiAgInByb2ZpbGVOYW1lIiA6ICJiY2QyMDMzYzYzZWM0YmY4IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzQyMzU0YTYwMTQ3YjdjY2Y1OWJhOTQ4OGZlNDFmNjM2YjYzNDFmOTJkZmY5YjM0MjIxNjA0MzNhN2VjNzI1N2QiLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ==";
8 | public static final String GUI_TESSERACT_OFF =
9 | "ewogICJ0aW1lc3RhbXAiIDogMTczMzcwNzAxMzY2MywKICAicHJvZmlsZUlkIiA6ICIzZGE2ZDgxOTI5MTY0MTNlODhlNzg2MjQ3NzA4YjkzZSIsCiAgInByb2ZpbGVOYW1lIiA6ICJGZXJTdGlsZSIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS81MjVkNDUzMGQwYjA5ODc2MzE0ZTE1NWYzMTViYTAyOWVmNDc4YTFhMzE2YzUyMWY1NzQzMWY1MmVlZTMxZmJiIiwKICAgICAgIm1ldGFkYXRhIiA6IHsKICAgICAgICAibW9kZWwiIDogInNsaW0iCiAgICAgIH0KICAgIH0KICB9Cn0=";
10 | public static final String GUI_FIST =
11 | "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTM3ZTMxNjY0NzhhY2Q3MDZhYjhiYTJkYjc4YzAzMzg2MTA4YTMwZWNjNjc4ZWMyNjRhMjdkZWZhZjAzNDA1OSJ9fX0=";
12 | public static final String GUI_XENOMORPH =
13 | "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjNjNzk1Y2U5ZWViNmRhNDMzMTU3MTZkNmNjYjg1Yjc4YWFmZTY2ZWRhMWJiY2Y5NjliMjAyODRiZTE5YTY1In19fQ==";
14 | public static final String GUI_BOOK =
15 | "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjgyMDllZWEwYjYyYjBkMWY1NmQ4YWZkNGY5Y2UyOGYzNDMzNDkwY2I2NmMzNWYxYTA5ZWI3MWNhNzY2ZmZkYiJ9fX0=";
16 | public static final String GUI_PLAYER =
17 | "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2ZhNTE0OGU4MjFjMWZiZTIzZDYzZDA4Nzc2YjA1NWIwNjU0MDI4ZTJlMzk2NWVjMGRjNDhiNGU2MmNlNmQwNSJ9fX0=";
18 | public static final String GUI_EARTH_RELOAD =
19 | "ewogICJ0aW1lc3RhbXAiIDogMTczMzc0NDc3OTc5MywKICAicHJvZmlsZUlkIiA6ICJjNmViMzdjNmE4YjM0MDI3OGJjN2FmZGE3ZjMxOWJmMyIsCiAgInByb2ZpbGVOYW1lIiA6ICJFbFJleUNhbGFiYXphbCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS8zMTc0MGFkYWJmYzFiOWVlZGI3N2MyNDQ0NWRlNDE1ZmRmZmMzNDFhMTEwODM5NGIxZDlkZmQ5YjMzMjRiNzY1IiwKICAgICAgIm1ldGFkYXRhIiA6IHsKICAgICAgICAibW9kZWwiIDogInNsaW0iCiAgICAgIH0KICAgIH0KICB9Cn0=";
20 | public static final String GUI_TV_TEXT =
21 | "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWFlYTNjYWM2MDk5YjY5ZDJjZjdjNWE2MWU1YTk1NDI5MTdmNDU4MzFmN2Y2OWRkZmFmMzJlNjc2ZGJiODhkYyJ9fX0=";
22 | public static final String GUI_RADIO =
23 | "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDE2Y2VlMTdhYTg1ZTAwNTgwMzIxNzU5YzE2ZGU4Y2Q0Nzc5MGY0Njk2MWFmY2ZiOGZiNWYzZWYyZjY2N2Y1NSJ9fX0=";
24 | public static final String GUI_PAINT_BUCKET =
25 | "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzdhOGJmZmYwMjY5YzI1YTVhYzQ1NDI4YzQ2ZWZjZTkyODYwODI0YzE0ZDNjNTRjMjg0MmU1ZGUxODJjIn19fQ==";
26 | public static final String GUI_LECTERN =
27 | "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWM4MGY4NjdkOWM4MGY2OWUxYjBhMzZhYmQ1ZGM2ZDYzNmJmYWQyYjYyZGYzMmUwYTA4MWJmZTkxMWQ3NTVkMiJ9fX0=";
28 | public static final String GUI_UNKNOWN_PLAYER =
29 | "ewogICJ0aW1lc3RhbXAiIDogMTczNDIzMjI0OTU3OSwKICAicHJvZmlsZUlkIiA6ICI1ZjU5NmViY2JlOTQ0NmQxYmI0M2JlNGYzZjRiOGJlNSIsCiAgInByb2ZpbGVOYW1lIiA6ICJUZWlsMHNzIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2FlNzgzZmMwNmRlOTRiYTU4YzYyMTc5MGNmMjMxYmZjNThhNGZhMGM1YjIwODdjN2IwOTY1NGI1YWM5YTc5YTIiLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ==";
30 |
31 | public static final String GUI_PREVIOUS_PAGE =
32 | "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMzEwODI5OGZmMmIyNjk1MWQ2ODNlNWFkZTQ2YTQyZTkwYzJmN2M3ZGQ0MWJhYTkwOGJjNTg1MmY4YzMyZTU4MyJ9fX0";
33 | public static final String GUI_PREVIOUS_PAGE_BLOCKED =
34 | "ewogICJ0aW1lc3RhbXAiIDogMTY0MDYxNjE5MjE0MiwKICAicHJvZmlsZUlkIiA6ICJmMjc0YzRkNjI1MDQ0ZTQxOGVmYmYwNmM3NWIyMDIxMyIsCiAgInByb2ZpbGVOYW1lIiA6ICJIeXBpZ3NlbCIsCiAgInNpZ25hdHVyZVJlcXVpcmVkIiA6IHRydWUsCiAgInRleHR1cmVzIiA6IHsKICAgICJTS0lOIiA6IHsKICAgICAgInVybCIgOiAiaHR0cDovL3RleHR1cmVzLm1pbmVjcmFmdC5uZXQvdGV4dHVyZS81MDgyMGY3NmUzZTA0MWM3NWY3NmQwZjMwMTIzMmJkZjQ4MzIxYjUzNGZlNmE4NTljY2I4NzNkMjk4MWE5NjIzIiwKICAgICAgIm1ldGFkYXRhIiA6IHsKICAgICAgICAibW9kZWwiIDogInNsaW0iCiAgICAgIH0KICAgIH0KICB9Cn0=";
35 | public static final String GUI_NEXT_PAGE =
36 | "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzg2MTg1YjFkNTE5YWRlNTg1ZjE4NGMzNGYzZjNlMjBiYjY0MWRlYjg3OWU4MTM3OGU0ZWFmMjA5Mjg3In19fQ";
37 | public static final String GUI_NEXT_PAGE_BLOCKED =
38 | "ewogICJ0aW1lc3RhbXAiIDogMTY0MDYxNjExMDQ4OCwKICAicHJvZmlsZUlkIiA6ICIxZjEyNTNhYTVkYTQ0ZjU5YWU1YWI1NmFhZjRlNTYxNyIsCiAgInByb2ZpbGVOYW1lIiA6ICJOb3RNaUt5IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdlNTc3MjBhNDg3OGM4YmNhYjBlOWM5YzQ3ZDllNTUxMjhjY2Q3N2JhMzQ0NWE1NGE5MWUzZTFlMWEyNzM1NmUiLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ==";
39 |
40 | public static final String GUI_CASTLE_OPEN =
41 | "ewogICJ0aW1lc3RhbXAiIDogMTczMzc5NDEzMzQ5OSwKICAicHJvZmlsZUlkIiA6ICI1ZjU5NmViY2JlOTQ0NmQxYmI0M2JlNGYzZjRiOGJlNSIsCiAgInByb2ZpbGVOYW1lIiA6ICJUZWlsMHNzIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzhkNmQ1ZWM3NzlhM2JkMThlZTllODM3MTJkZDU0MjE5YjQ5NjM3ZjkwZWVlMmJkMThiMjNkYTZkOTlmYjcyMWIiCiAgICB9CiAgfQp9";
42 | public static final String GUI_CASTLE_NORMAL =
43 | "ewogICJ0aW1lc3RhbXAiIDogMTczMzc5NDEzNTI5OSwKICAicHJvZmlsZUlkIiA6ICJlOThhZTBlMTI5MDg0ZDA5OTk0MTg4N2Q2YTk0ZTI2NCIsCiAgInByb2ZpbGVOYW1lIiA6ICJUYXVuYWhpWmVhbG90Qm90IiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzMxNjMyZDIwZTQ1YjNlNmE2YWFlZmQzMjgwZmRlMmIzNjdiYmU4NTk3NGUwMzk1YTViZmY1YWNkZWU0MWU2NzYiCiAgICB9CiAgfQp9";
44 | public static final String GUI_CASTLE_ALLY =
45 | "ewogICJ0aW1lc3RhbXAiIDogMTczMzczNDc2MjMzMCwKICAicHJvZmlsZUlkIiA6ICIwMzBlMDA1OWQwY2M0YTZhODY3N2RkZWU3MjEzMjg1MyIsCiAgInByb2ZpbGVOYW1lIiA6ICJTbXVnRm9vZGllIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlL2U4NTJhZjkyMjIxZDZlMDI5NDcxYjA5MWMxNTI5YmZkOWJkZTI3MWU4MTk3NWQxY2ZiNmNjNDkzMDg3MDQzNTYiCiAgICB9CiAgfQp9";
46 | public static final String GUI_CASTLE_ENEMY =
47 | "ewogICJ0aW1lc3RhbXAiIDogMTczMzczNDc2MDU1MiwKICAicHJvZmlsZUlkIiA6ICI5N2VmNDYyMzdhNGY0ZTQxYWY2ZTljYjg2MTdmNzc2OSIsCiAgInByb2ZpbGVOYW1lIiA6ICJZdWthcmlLYXplIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzczYThlY2QyYTRhMTMyNWVkNzM0MGJhNjgzMDdmZWZjOGI5MjQ0OWMzYTBiMzQxMzgyOTI4ZDIxODgzYjE4ODYiCiAgICB9CiAgfQp9";
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/command/DeclareCommand.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.command;
2 |
3 | import com.mojang.brigadier.arguments.StringArgumentType;
4 | import com.mojang.brigadier.context.CommandContext;
5 | import com.mojang.brigadier.exceptions.CommandSyntaxException;
6 | import com.mojang.brigadier.tree.LiteralCommandNode;
7 |
8 | import io.icker.factions.api.events.RelationshipEvents;
9 | import io.icker.factions.api.persistents.Faction;
10 | import io.icker.factions.api.persistents.Relationship;
11 | import io.icker.factions.util.Command;
12 | import io.icker.factions.util.Message;
13 |
14 | import net.minecraft.server.command.CommandManager;
15 | import net.minecraft.server.command.ServerCommandSource;
16 | import net.minecraft.server.network.ServerPlayerEntity;
17 | import net.minecraft.text.MutableText;
18 | import net.minecraft.text.Text;
19 | import net.minecraft.util.Formatting;
20 |
21 | import java.util.Locale;
22 |
23 | public class DeclareCommand implements Command {
24 | private int ally(CommandContext context) throws CommandSyntaxException {
25 | return updateRelationship(context, Relationship.Status.ALLY);
26 | }
27 |
28 | private int neutral(CommandContext context) throws CommandSyntaxException {
29 | return updateRelationship(context, Relationship.Status.NEUTRAL);
30 | }
31 |
32 | private int enemy(CommandContext context) throws CommandSyntaxException {
33 | return updateRelationship(context, Relationship.Status.ENEMY);
34 | }
35 |
36 | private int updateRelationship(
37 | CommandContext context, Relationship.Status status)
38 | throws CommandSyntaxException {
39 | String name = StringArgumentType.getString(context, "faction");
40 | ServerCommandSource source = context.getSource();
41 | ServerPlayerEntity player = source.getPlayerOrThrow();
42 |
43 | Faction targetFaction = Faction.getByName(name);
44 |
45 | if (targetFaction == null) {
46 | new Message(Text.translatable("factions.command.declare.fail.nonexistent_faction"))
47 | .fail()
48 | .send(player, false);
49 | return 0;
50 | }
51 |
52 | Faction sourceFaction = Command.getUser(player).getFaction();
53 |
54 | if (sourceFaction.equals(targetFaction)) {
55 | new Message(Text.translatable("factions.command.declare.fail.own_faction"))
56 | .fail()
57 | .send(player, false);
58 | return 0;
59 | }
60 |
61 | if (sourceFaction.getRelationship(targetFaction.getID()).status == status) {
62 | new Message(Text.translatable("factions.command.declare.fail.no_change"))
63 | .fail()
64 | .send(player, false);
65 | return 0;
66 | }
67 |
68 | Relationship.Status mutual = null;
69 |
70 | if (sourceFaction.getRelationship(targetFaction.getID()).status
71 | == targetFaction.getRelationship(sourceFaction.getID()).status) {
72 | mutual = sourceFaction.getRelationship(targetFaction.getID()).status;
73 | }
74 |
75 | Relationship rel = new Relationship(targetFaction.getID(), status);
76 | Relationship rev = targetFaction.getRelationship(sourceFaction.getID());
77 | sourceFaction.setRelationship(rel);
78 |
79 | RelationshipEvents.NEW_DECLARATION.invoker().onNewDecleration(rel);
80 |
81 | MutableText msgStatus =
82 | rel.status == Relationship.Status.ALLY
83 | ? Text.translatable("factions.command.declare.success.status.ally")
84 | .formatted(Formatting.GREEN)
85 | : rel.status == Relationship.Status.ENEMY
86 | ? Text.translatable("factions.command.declare.success.status.enemy")
87 | .formatted(Formatting.RED)
88 | : Text.translatable(
89 | "factions.command.declare.success.status.neutral");
90 |
91 | if (rel.status == rev.status) {
92 | RelationshipEvents.NEW_MUTUAL.invoker().onNewMutual(rel);
93 | new Message(
94 | Text.translatable(
95 | "factions.command.declare.success.mutual",
96 | msgStatus,
97 | targetFaction.getName()))
98 | .send(sourceFaction);
99 | new Message(
100 | Text.translatable(
101 | "factions.command.declare.success.mutual",
102 | msgStatus,
103 | sourceFaction.getName()))
104 | .send(targetFaction);
105 | return 1;
106 | } else if (mutual != null) {
107 | RelationshipEvents.END_MUTUAL.invoker().onEndMutual(rel, mutual);
108 | }
109 |
110 | new Message(
111 | Text.translatable(
112 | "factions.command.declare.success.actor",
113 | targetFaction.getName(),
114 | msgStatus))
115 | .send(sourceFaction);
116 |
117 | if (rel.status != Relationship.Status.NEUTRAL)
118 | new Message(
119 | Text.translatable(
120 | "factions.command.declare.success.subject",
121 | sourceFaction.getName(),
122 | msgStatus))
123 | .hover(Text.translatable("factions.command.declare.success.subject.hover"))
124 | .click(
125 | String.format(
126 | "/factions declare %s %s",
127 | rel.status.toString().toLowerCase(Locale.ROOT),
128 | sourceFaction.getName()))
129 | .send(targetFaction);
130 |
131 | return 1;
132 | }
133 |
134 | @Override
135 | public LiteralCommandNode getNode() {
136 | return CommandManager.literal("declare")
137 | .requires(Requires.isLeader())
138 | .then(
139 | CommandManager.literal("ally")
140 | .requires(Requires.hasPerms("factions.declare.ally", 0))
141 | .then(
142 | CommandManager.argument(
143 | "faction",
144 | StringArgumentType.greedyString())
145 | .suggests(Suggests.allFactions(false))
146 | .executes(this::ally)))
147 | .then(
148 | CommandManager.literal("neutral")
149 | .requires(Requires.hasPerms("factions.declare.neutral", 0))
150 | .then(
151 | CommandManager.argument(
152 | "faction",
153 | StringArgumentType.greedyString())
154 | .suggests(Suggests.allFactions(false))
155 | .executes(this::neutral)))
156 | .then(
157 | CommandManager.literal("enemy")
158 | .requires(Requires.hasPerms("factions.declare.enemy", 0))
159 | .then(
160 | CommandManager.argument(
161 | "faction",
162 | StringArgumentType.greedyString())
163 | .suggests(Suggests.allFactions(false))
164 | .executes(this::enemy)))
165 | .build();
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/util/SquareMapWrapper.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.util;
2 |
3 | import com.flowpowered.math.vector.Vector2i;
4 |
5 | import io.icker.factions.api.events.ClaimEvents;
6 | import io.icker.factions.api.events.FactionEvents;
7 | import io.icker.factions.api.persistents.Claim;
8 | import io.icker.factions.api.persistents.Faction;
9 | import io.icker.factions.api.persistents.Home;
10 |
11 | import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
12 |
13 | import xyz.jpenilla.squaremap.api.Key;
14 | import xyz.jpenilla.squaremap.api.MapWorld;
15 | import xyz.jpenilla.squaremap.api.Point;
16 | import xyz.jpenilla.squaremap.api.SimpleLayerProvider;
17 | import xyz.jpenilla.squaremap.api.Squaremap;
18 | import xyz.jpenilla.squaremap.api.SquaremapProvider;
19 | import xyz.jpenilla.squaremap.api.WorldIdentifier;
20 | import xyz.jpenilla.squaremap.api.marker.Icon;
21 | import xyz.jpenilla.squaremap.api.marker.Marker;
22 | import xyz.jpenilla.squaremap.api.marker.MarkerOptions;
23 |
24 | import java.awt.Color;
25 | import java.util.HashMap;
26 | import java.util.List;
27 | import java.util.Map;
28 | import java.util.Set;
29 | import java.util.UUID;
30 | import java.util.stream.Collectors;
31 |
32 | public class SquareMapWrapper {
33 | private HashMap layers = new HashMap<>();
34 | private Squaremap api;
35 |
36 | public SquareMapWrapper() {
37 | ClaimEvents.ADD.register(
38 | (Claim claim) -> {
39 | generateMarkers();
40 | });
41 | ClaimEvents.REMOVE.register(
42 | (x, z, level, faction) -> {
43 | generateMarkers();
44 | });
45 |
46 | ServerLifecycleEvents.SERVER_STARTED.register(
47 | (server) -> {
48 | this.api = SquaremapProvider.get();
49 |
50 | generateMarkers();
51 | });
52 |
53 | FactionEvents.SET_HOME.register(this::setHome);
54 | FactionEvents.MODIFY.register(faction -> generateMarkers());
55 | FactionEvents.MEMBER_JOIN.register((faction, user) -> generateMarkers());
56 | FactionEvents.MEMBER_LEAVE.register((faction, user) -> generateMarkers());
57 | FactionEvents.POWER_CHANGE.register((faction, oldPower) -> generateMarkers());
58 | FactionEvents.DISBAND.register((faction) -> generateMarkers());
59 | }
60 |
61 | private void generateMarkers() {
62 | for (SimpleLayerProvider layer : layers.values()) {
63 | for (Key id : layer.registeredMarkers().keySet()) {
64 | layer.removeMarker(id);
65 | }
66 | }
67 |
68 | for (Faction faction : Faction.all()) {
69 | Home home = faction.getHome();
70 | if (home != null) {
71 | setHome(faction, home);
72 | }
73 |
74 | String info = getInfo(faction);
75 | for (Map.Entry> entry :
76 | ClaimGrouper.separateClaimsByLevel(faction).entrySet()) {
77 | String level = entry.getKey();
78 | for (Map group :
79 | ClaimGrouper.convertClaimsToLineSegmentGroups(entry.getValue())) {
80 | List> outlines =
81 | ClaimGrouper.convertLineSegmentsToOutlines(group);
82 | List> points =
83 | outlines.stream()
84 | .map(
85 | (hole) ->
86 | hole.stream()
87 | .map(
88 | (point) ->
89 | Point.of(
90 | point.getX(),
91 | point.getY()))
92 | .collect(Collectors.toList()))
93 | .collect(Collectors.toList());
94 |
95 | SimpleLayerProvider layer = layers.get(level);
96 | if (layer == null) {
97 | layer =
98 | SimpleLayerProvider.builder("factions-" + level)
99 | .showControls(true)
100 | .build();
101 |
102 | MapWorld world =
103 | api.getWorldIfEnabled(WorldIdentifier.parse(level)).orElse(null);
104 | if (world != null) {
105 | world.layerRegistry()
106 | .register(Key.of("factions-" + level.replace(':', '-')), layer);
107 | }
108 |
109 | layers.put(level, layer);
110 | }
111 |
112 | Marker marker =
113 | Marker.polygon(points.removeFirst(), points)
114 | .markerOptions(
115 | MarkerOptions.builder()
116 | .fillColor(
117 | new Color(
118 | faction.getColor()
119 | .getColorValue()))
120 | .strokeColor(
121 | new Color(
122 | faction.getColor()
123 | .getColorValue()))
124 | .hoverTooltip(faction.getName())
125 | .clickTooltip(info));
126 |
127 | layer.addMarker(Key.of(UUID.randomUUID().toString()), marker);
128 | }
129 | }
130 | }
131 | }
132 |
133 | private void setHome(Faction faction, Home home) {
134 | if (home == null) {
135 | for (Map.Entry entry : layers.entrySet()) {
136 | entry.getValue().removeMarker(Key.of(faction.getID().toString() + "-home"));
137 | }
138 | return;
139 | }
140 |
141 | SimpleLayerProvider layer = layers.get(home.level);
142 |
143 | if (layer == null) {
144 | layer =
145 | SimpleLayerProvider.builder("factions-" + home.level)
146 | .showControls(true)
147 | .build();
148 |
149 | MapWorld world = api.getWorldIfEnabled(WorldIdentifier.parse(home.level)).orElse(null);
150 | if (world != null) {
151 | world.layerRegistry()
152 | .register(Key.of("factions-" + home.level.replace(':', '-')), layer);
153 | } else {
154 | }
155 |
156 | layers.put(home.level, layer);
157 | }
158 |
159 | for (Map.Entry entry : layers.entrySet()) {
160 | if (entry.getKey().equals(home.level)) {
161 | continue;
162 | }
163 |
164 | entry.getValue().removeMarker(Key.of(faction.getID().toString() + "-home"));
165 | }
166 |
167 | Marker marker = layer.registeredMarkers().get(Key.of(faction.getID().toString() + "-home"));
168 |
169 | if (marker == null) {
170 | Marker homeMarker =
171 | Marker.icon(Point.of(home.x, home.z), Key.of("squaremap-spawn_icon"), 16)
172 | .markerOptions(
173 | MarkerOptions.builder()
174 | .clickTooltip(getInfo(faction))
175 | .hoverTooltip(faction.getName() + "'s Home"));
176 | layer.addMarker(Key.of(faction.getID().toString() + "-home"), homeMarker);
177 | } else {
178 | ((Icon) marker).point(Point.of(home.x, home.z));
179 | }
180 | }
181 |
182 | private String getInfo(Faction faction) {
183 | return "Name: "
184 | + faction.getName()
185 | + "
"
186 | + "Description: "
187 | + faction.getDescription()
188 | + "
"
189 | + "Power: "
190 | + faction.getPower()
191 | + "
"
192 | + "Number of members: "
193 | + faction.getUsers().size(); // + "
"
194 | // + "Allies: " + Ally.getAllies(faction.getName).stream().map(ally ->
195 | // ally.target).collect(Collectors.joining(", "));
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/ui/PagedGui.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.ui;
2 |
3 | import eu.pb4.sgui.api.elements.GuiElement;
4 | import eu.pb4.sgui.api.elements.GuiElementBuilder;
5 | import eu.pb4.sgui.api.elements.GuiElementBuilderInterface;
6 | import eu.pb4.sgui.api.elements.GuiElementInterface;
7 | import eu.pb4.sgui.api.gui.SimpleGui;
8 |
9 | import io.icker.factions.util.GuiInteract;
10 | import io.icker.factions.util.Icons;
11 |
12 | import net.minecraft.item.ItemStack;
13 | import net.minecraft.item.Items;
14 | import net.minecraft.screen.ScreenHandlerType;
15 | import net.minecraft.screen.slot.Slot;
16 | import net.minecraft.server.network.ServerPlayerEntity;
17 | import net.minecraft.sound.SoundEvents;
18 | import net.minecraft.text.Text;
19 | import net.minecraft.util.Formatting;
20 |
21 | import org.jetbrains.annotations.ApiStatus;
22 | import org.jetbrains.annotations.Nullable;
23 |
24 | // Shamelessly stolen from Get off My Lawn https://github.com/Patbox/get-off-my-lawn-reserved
25 | @ApiStatus.Internal
26 | public abstract class PagedGui extends SimpleGui {
27 | public static final int PAGE_SIZE = 9 * 4;
28 | protected final Runnable closeCallback;
29 | protected int page = 0;
30 | public boolean ignoreCloseCallback;
31 |
32 | public PagedGui(ServerPlayerEntity player, @Nullable Runnable closeCallback) {
33 | super(ScreenHandlerType.GENERIC_9X5, player, false);
34 | this.closeCallback = closeCallback;
35 | }
36 |
37 | public void refreshOpen() {
38 | this.updateDisplay();
39 | this.open();
40 | }
41 |
42 | @Override
43 | public void onClose() {
44 | if (this.closeCallback != null && !ignoreCloseCallback) {
45 | this.closeCallback.run();
46 | }
47 | }
48 |
49 | protected void nextPage() {
50 | this.page = Math.min(this.getPageAmount() - 1, this.page + 1);
51 | this.updateDisplay();
52 | }
53 |
54 | protected boolean canNextPage() {
55 | return this.getPageAmount() > this.page + 1;
56 | }
57 |
58 | protected void previousPage() {
59 | this.page = Math.max(0, this.page - 1);
60 | this.updateDisplay();
61 | }
62 |
63 | protected boolean canPreviousPage() {
64 | return this.page - 1 >= 0;
65 | }
66 |
67 | protected void updateDisplay() {
68 | var offset = this.page * PAGE_SIZE;
69 |
70 | for (int i = 0; i < PAGE_SIZE; i++) {
71 | var element = this.getElement(offset + i);
72 |
73 | if (element == null) {
74 | element = DisplayElement.empty();
75 | }
76 |
77 | if (element.element() != null) {
78 | this.setSlot(i, element.element());
79 | } else if (element.slot() != null) {
80 | this.setSlotRedirect(i, element.slot());
81 | }
82 | }
83 |
84 | for (int i = 0; i < 9; i++) {
85 | var navElement = this.getNavElement(i);
86 |
87 | if (navElement == null) {
88 | navElement = DisplayElement.EMPTY;
89 | }
90 |
91 | if (navElement.element != null) {
92 | this.setSlot(i + PAGE_SIZE, navElement.element);
93 | } else if (navElement.slot != null) {
94 | this.setSlotRedirect(i + PAGE_SIZE, navElement.slot);
95 | }
96 | }
97 | }
98 |
99 | protected int getPage() {
100 | return this.page;
101 | }
102 |
103 | protected abstract int getPageAmount();
104 |
105 | protected abstract DisplayElement getElement(int id);
106 |
107 | protected DisplayElement getNavElement(int id) {
108 | return switch (id) {
109 | case 1 -> DisplayElement.previousPage(this);
110 | case 3 -> DisplayElement.nextPage(this);
111 | case 7 ->
112 | DisplayElement.of(
113 | new GuiElementBuilder(Items.STRUCTURE_VOID)
114 | .setName(
115 | Text.translatable(
116 | this.closeCallback != null
117 | ? "factions.gui.generic.back"
118 | : "factions.gui.generic.close")
119 | .formatted(Formatting.RED))
120 | .hideDefaultTooltip()
121 | .setCallback(
122 | (x, y, z) -> {
123 | playClickSound(this.player);
124 | this.close(this.closeCallback != null);
125 | }));
126 | default -> DisplayElement.filler();
127 | };
128 | }
129 |
130 | public record DisplayElement(@Nullable GuiElementInterface element, @Nullable Slot slot) {
131 | private static final DisplayElement EMPTY =
132 | DisplayElement.of(
133 | new GuiElement(ItemStack.EMPTY, GuiElementInterface.EMPTY_CALLBACK));
134 | private static final DisplayElement FILLER =
135 | DisplayElement.of(
136 | new GuiElementBuilder(Items.WHITE_STAINED_GLASS_PANE)
137 | .setName(Text.empty())
138 | .hideTooltip());
139 |
140 | public static DisplayElement of(GuiElementInterface element) {
141 | return new DisplayElement(element, null);
142 | }
143 |
144 | public static DisplayElement of(GuiElementBuilderInterface> element) {
145 | return new DisplayElement(element.build(), null);
146 | }
147 |
148 | public static DisplayElement of(Slot slot) {
149 | return new DisplayElement(null, slot);
150 | }
151 |
152 | public static DisplayElement nextPage(PagedGui gui) {
153 | if (gui.canNextPage()) {
154 | return DisplayElement.of(
155 | new GuiElementBuilder(Items.PLAYER_HEAD)
156 | .setName(
157 | Text.translatable("factions.gui.generic.next_page")
158 | .formatted(Formatting.WHITE))
159 | .hideDefaultTooltip()
160 | .setProfileSkinTexture(Icons.GUI_NEXT_PAGE)
161 | .setCallback(
162 | (x, y, z) -> {
163 | playClickSound(gui.player);
164 | gui.nextPage();
165 | }));
166 | } else {
167 | return DisplayElement.of(
168 | new GuiElementBuilder(Items.PLAYER_HEAD)
169 | .setName(
170 | Text.translatable("factions.gui.generic.next_page")
171 | .formatted(Formatting.DARK_GRAY))
172 | .hideDefaultTooltip()
173 | .setProfileSkinTexture(Icons.GUI_NEXT_PAGE_BLOCKED));
174 | }
175 | }
176 |
177 | public static DisplayElement previousPage(PagedGui gui) {
178 | if (gui.canPreviousPage()) {
179 | return DisplayElement.of(
180 | new GuiElementBuilder(Items.PLAYER_HEAD)
181 | .setName(
182 | Text.translatable("factions.gui.generic.previous_page")
183 | .formatted(Formatting.WHITE))
184 | .hideDefaultTooltip()
185 | .setProfileSkinTexture(Icons.GUI_PREVIOUS_PAGE)
186 | .setCallback(
187 | (x, y, z) -> {
188 | playClickSound(gui.player);
189 | gui.previousPage();
190 | }));
191 | } else {
192 | return DisplayElement.of(
193 | new GuiElementBuilder(Items.PLAYER_HEAD)
194 | .setName(
195 | Text.translatable("factions.gui.generic.previous_page")
196 | .formatted(Formatting.DARK_GRAY))
197 | .hideDefaultTooltip()
198 | .setProfileSkinTexture(Icons.GUI_PREVIOUS_PAGE_BLOCKED));
199 | }
200 | }
201 |
202 | public static DisplayElement filler() {
203 | return FILLER;
204 | }
205 |
206 | public static DisplayElement empty() {
207 | return EMPTY;
208 | }
209 | }
210 |
211 | public static final void playClickSound(ServerPlayerEntity player) {
212 | GuiInteract.playSound(player, SoundEvents.UI_BUTTON_CLICK.value(), 1f, 1f);
213 | }
214 | }
215 |
--------------------------------------------------------------------------------
/src/main/java/io/icker/factions/command/InfoCommand.java:
--------------------------------------------------------------------------------
1 | package io.icker.factions.command;
2 |
3 | import com.mojang.authlib.GameProfile;
4 | import com.mojang.brigadier.arguments.StringArgumentType;
5 | import com.mojang.brigadier.context.CommandContext;
6 | import com.mojang.brigadier.exceptions.CommandSyntaxException;
7 | import com.mojang.brigadier.tree.LiteralCommandNode;
8 |
9 | import io.icker.factions.FactionsMod;
10 | import io.icker.factions.api.persistents.Faction;
11 | import io.icker.factions.api.persistents.User;
12 | import io.icker.factions.ui.InfoGui;
13 | import io.icker.factions.util.Command;
14 | import io.icker.factions.util.Message;
15 |
16 | import net.minecraft.server.GameProfileResolver;
17 | import net.minecraft.server.command.CommandManager;
18 | import net.minecraft.server.command.ServerCommandSource;
19 | import net.minecraft.server.network.ServerPlayerEntity;
20 | import net.minecraft.text.Text;
21 | import net.minecraft.util.Formatting;
22 | import net.minecraft.util.Util;
23 |
24 | import java.util.List;
25 | import java.util.stream.Collectors;
26 |
27 | public class InfoCommand implements Command {
28 | private int self(CommandContext context) throws CommandSyntaxException {
29 | ServerCommandSource source = context.getSource();
30 | ServerPlayerEntity player = source.getPlayerOrThrow();
31 |
32 | User user = Command.getUser(player);
33 | if (!user.isInFaction()) {
34 | new Message(Text.translatable("factions.command.info.fail.no_faction"))
35 | .fail()
36 | .send(player, false);
37 | return 0;
38 | }
39 |
40 | return info(player, user.getFaction());
41 | }
42 |
43 | private int any(CommandContext context) throws CommandSyntaxException {
44 | String factionName = StringArgumentType.getString(context, "faction");
45 |
46 | ServerCommandSource source = context.getSource();
47 | ServerPlayerEntity player = source.getPlayerOrThrow();
48 |
49 | Faction faction = Faction.getByName(factionName);
50 | if (faction == null) {
51 | new Message(Text.translatable("factions.command.info.fail.nonexistent_faction"))
52 | .fail()
53 | .send(player, false);
54 | return 0;
55 | }
56 |
57 | return info(player, faction);
58 | }
59 |
60 | public static int info(ServerPlayerEntity player, Faction faction) {
61 | if (FactionsMod.CONFIG.GUI) {
62 | new InfoGui(player, faction, null);
63 | return 1;
64 | }
65 | List users = faction.getUsers();
66 |
67 | GameProfileResolver resolver =
68 | player.getEntityWorld().getServer().getApiServices().profileResolver();
69 | String owner =
70 | Formatting.WHITE
71 | + users.stream()
72 | .filter(u -> u.rank == User.Rank.OWNER)
73 | .map(
74 | user ->
75 | resolver.getProfileById(user.getID())
76 | .orElse(
77 | new GameProfile(
78 | Util.NIL_UUID,
79 | "{Uncached Player}"))
80 | .name())
81 | .collect(Collectors.joining(", "));
82 |
83 | String usersList =
84 | users.stream()
85 | .map(
86 | user ->
87 | resolver.getProfileById(user.getID())
88 | .orElse(
89 | new GameProfile(
90 | Util.NIL_UUID, "{Uncached Player}"))
91 | .name())
92 | .collect(Collectors.joining(", "));
93 |
94 | String mutualAllies =
95 | faction.getMutualAllies().stream()
96 | .map(rel -> Faction.get(rel.target))
97 | .map(fac -> fac.getColor() + fac.getName())
98 | .collect(Collectors.joining(Formatting.GRAY + ", "));
99 |
100 | String enemiesWith =
101 | Formatting.GRAY
102 | + faction.getEnemiesWith().stream()
103 | .map(rel -> Faction.get(rel.target))
104 | .map(fac -> fac.getColor() + fac.getName())
105 | .collect(Collectors.joining(Formatting.GRAY + ", "));
106 |
107 | int requiredPower = faction.getClaims().size() * FactionsMod.CONFIG.POWER.CLAIM_WEIGHT;
108 | int maxPower =
109 | users.size() * FactionsMod.CONFIG.POWER.MEMBER + FactionsMod.CONFIG.POWER.BASE;
110 |
111 | // generate the ---
112 | int numDashes = 32 - faction.getName().length();
113 | String dashes =
114 | new StringBuilder("--------------------------------").substring(0, numDashes / 2);
115 |
116 | new Message(
117 | Formatting.BLACK
118 | + dashes
119 | + "[ "
120 | + faction.getColor()
121 | + faction.getName()
122 | + Formatting.BLACK
123 | + " ]"
124 | + dashes)
125 | .send(player, false);
126 | new Message(Text.translatable("factions.gui.info.description").formatted(Formatting.GOLD))
127 | .add(Formatting.WHITE + faction.getDescription())
128 | .send(player, false);
129 | new Message(Text.translatable("factions.gui.info.owner").formatted(Formatting.GOLD))
130 | .add(Formatting.WHITE + owner)
131 | .send(player, false);
132 | new Message(
133 | Text.translatable(
134 | "factions.gui.info.members",
135 | Text.literal(Integer.toString(users.size()))
136 | .formatted(Formatting.WHITE))
137 | .formatted(Formatting.GOLD))
138 | .add(usersList)
139 | .send(player, false);
140 | new Message(Text.translatable("factions.gui.info.power").formatted(Formatting.GOLD))
141 | .add(
142 | Formatting.GREEN.toString()
143 | + faction.getPower()
144 | + slash()
145 | + requiredPower
146 | + slash()
147 | + maxPower)
148 | .hover(Text.translatable("factions.gui.info.power.description"))
149 | .send(player, false);
150 | new Message(
151 | Text.translatable(
152 | "factions.gui.info.allies.some",
153 | Text.literal(
154 | Integer.toString(
155 | faction.getMutualAllies().size()))
156 | .formatted(Formatting.WHITE))
157 | .formatted(Formatting.GREEN))
158 | .add(mutualAllies)
159 | .send(player, false);
160 | new Message(
161 | Text.translatable(
162 | "factions.gui.info.enemies.some",
163 | Text.literal(
164 | Integer.toString(
165 | faction.getEnemiesWith().size()))
166 | .formatted(Formatting.WHITE))
167 | .formatted(Formatting.RED))
168 | .add(enemiesWith)
169 | .send(player, false);
170 |
171 | return 1;
172 | }
173 |
174 | private static String slash() {
175 | return Formatting.GRAY + " / " + Formatting.GREEN;
176 | }
177 |
178 | public LiteralCommandNode getNode() {
179 | return CommandManager.literal("info")
180 | .requires(Requires.hasPerms("factions.info", 0))
181 | .executes(this::self)
182 | .then(
183 | CommandManager.argument("faction", StringArgumentType.greedyString())
184 | .requires(Requires.hasPerms("factions.info.other", 0))
185 | .suggests(Suggests.allFactions())
186 | .executes(this::any))
187 | .build();
188 | }
189 | }
190 |
--------------------------------------------------------------------------------